Bidirectional real-time file sync with Teleport Beams.
beamup keeps a local directory and a Beam's filesystem in sync — changes on either side propagate to the other in near-realtime. It manages the full beam lifecycle (create, sync, destroy) and is designed to recover gracefully from the inherent instability of ephemeral VMs.
beamup runs two components:
- Local CLI (
beamup) — watches your project directory via FSEvents, communicates with the remote agent over a persistenttsh beams execpipe - Remote agent (
beamup-agent) — a small static binary deployed into the beam, watches the remote filesystem via inotify, relays changes back
Communication uses length-prefixed msgpack frames over stdin/stdout of the tsh beams exec process. No ports consumed, no direct IP needed.
brew install webvictim/tap/beamup./scripts/build.shThis cross-compiles the agent for linux/arm64, embeds it into the CLI binary, and produces target/release/beamup — a single self-contained binary.
Requires cross for the agent cross-compilation. To build just the CLI without embedding (for development):
cargo buildTo cross-compile only the agent binary (without building the CLI):
cross build --release --target aarch64-unknown-linux-musl -p beamup-agentThis produces target/aarch64-unknown-linux-musl/release/beamup-agent. Subsequent cargo build of the CLI will automatically embed it.
# Create a beam, deploy the agent, and start syncing the current directory
beamup start
# Sync with an existing beam
beamup start --beam kinetic-vault
# Sync a specific directory
beamup start --local-path ~/projects/myapp
# Start sync and immediately drop into a console on the beam
beamup start --console
# One-way sync: push local to beam, then only pull back changes
beamup start --initial-sync local-to-beam --ongoing-sync beam-to-local
# Push-only sync (no changes pulled back from beam)
beamup start --initial-sync local-to-beam --ongoing-sync local-to-beam
# Oneshot mode: sync files, print beam name, exit (for scripting/agents)
beamup -q start --oneshot --local-path ~/projects/myapp
# Use a Machine ID identity file instead of interactive tsh login
beamup -i /path/to/identity --proxy cluster.example.com:443 start --local-path ~/projects/myapp
# Check sync status
beamup status
# Run a command in the beam
beamup exec -- cargo test
# Stop syncing and destroy the beam
beamup down
# Stop syncing but keep the beam alive
beamup down --keep-beam--oneshot performs the initial sync and exits immediately, printing only the beam name to stdout. Combined with -q (quiet), this suppresses all log output — making beamup usable as a tool that provisions a beam with a file structure for downstream automation.
# Create a beam, sync files, print beam name, exit
BEAM=$(beamup -q start --oneshot --local-path ~/projects/myapp)
echo "Beam ready: $BEAM"
tsh beams exec "$BEAM" -- make build--oneshot is mutually exclusive with --ongoing-sync (there is no ongoing sync in this mode).
beamup supports Teleport Machine ID (tbot) for non-interactive authentication. Pass an identity file with --identity / -i and the proxy address with --proxy:
beamup -i /path/to/identity --proxy cluster.example.com:443 start --local-path ~/projectThis is required for headless/automated usage where no interactive tsh login session exists. The TELEPORT_IDENTITY_FILE env var is also respected by tsh directly.
The --concurrency / -c flag controls how many parallel SCP transfers run simultaneously (default: 8). Higher values can significantly improve throughput:
| Concurrency | Approx. throughput |
|---|---|
| 8 | ~22 MB/s |
| 12 | ~29 MB/s |
| 16 | ~35 MB/s |
| 24 | ~44 MB/s |
# Faster transfers with higher concurrency
beamup start -c 16 --local-path ~/projects/myappThe default of 8 is conservative for stability — higher values can trigger tsh reauthentication errors under interactive login sessions. Transfers are retried automatically on failure, but frequent retries slow things down. Machine ID identity files are more stable at high concurrency since they don't require interactive reauthentication.
By default, beamup syncs bidirectionally. You can control the direction independently for the initial sync and ongoing sync:
| Flag | Values | Default |
|---|---|---|
--initial-sync |
local-to-beam, beam-to-local, bidirectional |
bidirectional |
--ongoing-sync |
local-to-beam, beam-to-local, bidirectional |
bidirectional |
Common patterns:
- Dev on beam:
--initial-sync local-to-beam --ongoing-sync bidirectional— push code up, then sync both ways - Build on beam:
--initial-sync local-to-beam --ongoing-sync beam-to-local— push code up, only pull back artifacts - Push-only mirror:
--ongoing-sync local-to-beam— beam is a read-only mirror of local
- Bidirectional sync — local edits push to beam, beam edits pull to local
- Configurable direction — one-way or bidirectional, independently for initial and ongoing sync
- Progress bar — real-time transfer progress with per-chunk updates, transfer rate, and ETA
- Console mode —
--consoledrops you into a beam shell after sync completes - Near-realtime — sub-second propagation via OS-native file watching
- Conflict detection — when both sides edit the same file, both versions are preserved (
.local.conflictsuffix) and the user is alerted - Respects .gitignore — plus an optional
.beamignorefor additional exclusions - Atomic writes — write-to-temp-then-rename prevents partial file corruption
- Heartbeat monitoring — detects beam death within 15 seconds
- Large file chunking — files over 64MB are split into chunks, compressed, and transferred in parallel
┌─────────────┐ tsh beams exec ┌───────────────┐
│ beamup │ ◄── stdin/stdout pipe ──► │ beamup-agent │
│ (macOS) │ msgpack length-prefixed │ (linux/arm64) │
│ │ frames │ │
│ FSEvents │ │ inotify │
│ watcher │ │ watcher │
└─────────────┘ └───────────────┘
crates/
├── beamup-protocol/ Shared message types, codec, hashing, ignore rules
├── beamup-cli/ Local CLI binary (macOS)
└── beamup-agent/ Remote agent binary (linux/arm64)
tshCLI with Beams support (authenticated viatsh login)- Rust toolchain for building
- cross for cross-compiling the agent (
cargo install cross --git https://github.com/cross-rs/cross) - Docker (required by
crossto run the linux/arm64 build container)
Apache-2.0