Operating model
Tunnels and security
Keep internal services off the internet and reach them as localhost through SSH.
Reviewed 2026-07-25
Chalupa reduces the public attack surface in two ways: it rewrites Compose ports to listen on loopback, and it uses an outbound telemetry flow. Only SSH needs to accept inbound traffic.
How ports are rewritten
This Compose mapping:
services:
api:
ports:
- "8080:8080"
becomes this mapping on the droplet:
services:
api:
ports:
- "127.0.0.1:8080:8080"
The process does not invent a second port list. chalupa tunnel consumes
outputs derived from the same Compose file.
Open the tunnel
Keep the absolute config path:
export DEMO_CONFIG="/path/to/project/chalupa.yml"
chalupa tunnel --config "$DEMO_CONFIG"
Conceptually, SSH creates one rule per port:
ssh -N -L 8080:localhost:8080 root@192.0.2.44
192.0.2.44 belongs to an RFC 5737 documentation range. The task reads the
real address from Pulumi output; do not copy it into public documentation.
For an interactive shell on the droplet, use the same boundary instead of a hand-written SSH command:
chalupa ssh --config "$DEMO_CONFIG"
It resolves the address from the same Pulumi output, validates an optional dedicated identity, and never forwards your SSH agent. Chalupa deliberately publishes no per-environment hostname: real addresses stay out of control-plane metadata, and the control plane cannot proxy an SSH session.
While SSH remains active:
http://localhost:8080reaches the remote API;- a local client can use
localhostfor the database; - suites do not need to know the droplet IP.
What is exposed
The expected result is:
| Surface | Exposure |
|---|---|
| SSH | public droplet interface |
| Service ports | remote 127.0.0.1 |
| Heartbeats | outbound HTTPS from the agent |
| Local Pulumi state | remains on the operator's machine |
Review the preview to verify every bind. A provider firewall adds another layer of defense; it does not replace this review.
SSH keys
Use managed keys and limit who can start tunnels. Do not permanently disable host verification. The first connection can explicitly accept a new key; investigate any later change.
A dedicated Chalupa identity avoids reusing a key from another provider or purpose. Add its public half to the DigitalOcean account and keep the private half only on the operator machine. Then select it explicitly:
export SSH_IDENTITY="$HOME/.ssh/id_ed25519_chalupa"
chalupa tunnel --config "$DEMO_CONFIG"
SSH_IDENTITY is a Taskfile input read from the environment, so the CLI
forwards it without needing a flag. It must expand to an absolute path.
Chalupa accepts only an
operator-owned, unencrypted regular private-key file recognized by the system
OpenSSH implementation with mode 0400 or 0600, rejects symlinks, and
invokes OpenSSH with IdentitiesOnly=yes. Supply the same input to up,
agent-bootstrap, and down so bootstrap and final telemetry/log flushes use
the same identity. Leaving it unset preserves normal OpenSSH agent and
default-key behavior.
Deleting a public key from the DigitalOcean account does not remove it from
an already-created droplet's authorized_keys. For ephemeral environments,
rotate by registering the replacement public key, recreating compute with the
replacement selected in ssh.keys, verifying access, and only then removing
the old account key. Never store a private key in DigitalOcean, Chalupa
configuration, tvault, Vercel, or Git.
The remote account and keys are outside the Chalupa console's scope. The console shows commands, but it does not store private keys.
Secrets
Treat these values as secrets:
- provider API token;
- ingest HMAC key;
- Chalupa administrator token;
- service-specific secrets.
They must not appear in:
chalupa.yml;- URLs or query parameters;
- suite-run payloads;
- logs or screenshots;
- public metadata;
- commits.
The agent receives only the credential it needs to sign ingest requests. The control plane uses a separate read-only credential for cost estimates. The current version does not give the droplet a deletion credential.
Signed signals
The agent signs the exact body before sending it. The control plane verifies the signature and time window before it persists the observation.
HTTPS protects the transport; HMAC authenticates the sender and detects changes. Rotate the key like any other fleet secret. The read API never returns it.
The console is not an execution panel
The console UI does not include buttons to launch or sink resources. Operator
actions remain in the terminal, behind the chalupa CLI and the Taskfile it
delegates to, where the context and typed confirmation are visible. A separate
trusted worker may enforce an expired idleShutdownMinutes policy with its
dedicated write credential; it rechecks the provider identity and Chalupa tag,
never runs in the browser, and cannot target the protected data stack.