Skip to content

AFK-surf/fly-vault

Repository files navigation

fly-vault

Remote development VM tooling for Fly.io with attestation-gated access and token authentication.

What This Is

fly-vault is a Rust workspace with five binaries:

  • fly-vault (client): runs on your laptop, verifies attestation, authenticates with access_token, opens console/port forwards.
  • fly-vault-admin (deployment admin): runs on your laptop or CI, manages tenant machines in vault-tenants via the Fly Machines API.
  • init (server): runs as the Fly Machine entrypoint, serves QUIC, provisions rootfs to /data/rootfs, preserves /root and /home under /data/persist, and boots the workload.
  • vault-proxy (UDP multiplexer): routes client UDP packets to tenant machines by machine id.
  • shared protocol crate: stream tags, control/console framing, shared wire types.

The design is documented in DESIGN.md.

Security Model Summary

  • Attestation authenticity: client verifies Fly OIDC JWT signature and checks iss + aud channel binding + app_name.
  • Transport security: QUIC (TLS 1.3).
  • Authentication: single ACCESS_TOKEN used for initial provisioning and reconnects.
  • Data at rest: the VM rootfs plus persistent /root and /home state are stored on the Fly volume (/data/rootfs and /data/persist) without client-side disk encryption.

Important: because data is not encrypted client-side, Fly platform operators with host/platform access can read persisted VM data.

Repository Layout

  • crates/protocol: stream tags, control/console framing, shared wire types.
  • crates/init: VM-side server, setup manager, forwarding.
  • crates/client: CLI client, attestation verification, console/forwarding.
  • crates/admin: deployment admin CLI for tenant create/list/delete/update-image.
  • crates/proxy: UDP proxy service for multi-tenant routing.
  • docs/: protocol/API references used by the implementation.

Build

cargo build

Build static init binary for container image:

cargo zigbuild --release --target x86_64-unknown-linux-musl -p init

Test

cargo test
cargo test -p init --bin init

Client Configuration

Config path:

  • ~/.config/fly-vault/config.toml

Example:

[vault.my-dev]
address = "[fdaa:x:x::x]:8443"
org = "my-org"
app = "my-dev-vault"

#[vault.my-proxy]
#address = "proxy.fly.dev:443"
#org = "my-org"
#app = "my-proxy-vault"
#machine_id = "148e21ea7e46e8"

access_token = "secret-token-here"
forward = ["8080:localhost:8080"]
rootfs = "~/.config/fly-vault/rootfs/dev-env.tar.gz"
# Alternative: let init download a tar.gz directly
# rootfs_url = "https://example.com/dev-env.tar.gz"

Client CLI Commands

fly-vault connect <vault-name>
fly-vault connect <vault-name> --forward 3000:localhost:3000
fly-vault connect <vault-name> --reprovision
fly-vault exec <vault-name> -- ls -lash /
fly-vault build

--reprovision sends a new rootfs tarball to a ready VM after successful token authentication. The reprovision replaces the extracted rootfs in place but preserves /root and /home.

For local development via Cargo:

cargo run -p fly-vault -- connect <vault-name>
cargo run -p fly-vault -- exec <vault-name> -- uname -a

Deployment Admin CLI (fly-vault-admin)

Auth/config:

  • --api-token or FLY_API_TOKEN (required)
  • --app or FLY_APP (required)
  • --api-base or FLY_API_BASE (defaults to https://api.machines.dev)

Tenant template snippet:

version = 1
app = "vault-tenants"
machine_count = 1

[machine]
name = "tenant-{{tenant_id}}-{{index}}"
region = "ord"

[machine.config]
image = "registry.fly.io/vault-tenants/init:latest"

[[machine.config.mounts]]
path = "/data"
name = "tvol_{{tenant_id}}_{{index}}"

[machine.config.env]
ACCESS_TOKEN = "{{access_token}}"

Supported template variables:

  • {{tenant_id}}
  • {{index}} (1-based machine index)
  • {{access_token}} (if --access-token is supplied)
  • extra values from repeated --var key=value

Commands:

fly-vault-admin tenant create acme --template ./tenant.template.toml --access-token secret
fly-vault-admin tenant create acme --template ./tenant.template.toml --dry-run
fly-vault-admin tenant list --wide
fly-vault-admin tenant list --json
fly-vault-admin tenant usage-facts --tenant acme
fly-vault-admin tenant usage-facts --listen 127.0.0.1:18083 --bearer-token secret
fly-vault-admin tenant delete acme --yes
fly-vault-admin tenant update-image --image registry.fly.io/vault-tenants/init:latest --all-tenants

tenant usage-facts emits a flat array of machine usage facts, and tenant usage-facts --listen ... serves the same payload at GET /usage-facts with optional bearer auth plus a GET /healthz liveness route.

Each usage fact includes:

  • machine_id, state, region, instance_id
  • started_at, stopped_at, deleted_at
  • metadata
  • volumes[].volume_id
  • volumes[].size_gib

This JSON shape is intended to be consumed by upstream metering adapters such as unbox, rather than relying on the broader tenant list --json output.

About

Confidential remote VM on Fly.io

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages