One Server, Whitelisted Commands, Zero Shared Keys
Tacavar's deploy gate replaces shared root SSH keys: whitelist dispatcher, sudo-scoped commands, and append-only audit logging for cross-server RPC.
Sharing root SSH keys was the obvious answer. I built a whitelist gate instead.
The Problem: Bailian Needs to Trigger Primary Droplet Jobs
Tacavar runs two droplets in production: Bailian and primary. Bailian is the orchestration node. It watches queues, schedules content, and decides when work exists. The primary droplet is where the heavy jobs live: deploy the Tacavar app, post an Instagram reel, write a video brief, warm caches, run migrations. The split keeps orchestration responsive while production stays on a box with more disk, more CPU, and the actual runtime.
The problem is not 'how do I SSH from A to B.' The problem is 'how do I let a service on Bailian trigger a narrow set of production jobs without handing it the machine.' The first version of that answer is a root SSH key on Bailian that can log into primary. That works until it doesn't. Bailian is internet-facing enough to be a target. Its logs contain API keys, tokens, and webhook payloads. If someone gets code execution on Bailian, a shared root key turns a compromised orchestrator into a fully compromised production droplet. That is not cross-server RPC security. That is lateral movement with extra steps.
So the requirement was specific: Bailian must be able to invoke named jobs on primary, with arguments, and get results back, but it must not be able to run arbitrary bash. It must not install packages unless the command says so. It must not read /etc/shadow. It must not open a reverse shell. It must not become root on primary just because it can trigger a deploy.
Why Shared Root SSH Keys Were the Wrong Default
Shared root SSH keys fail at the exact place operators need control. They grant a capability that is too broad for the task. A deploy key that can run git pull also can run rm -rf /. A key that can post an Instagram reel also can dump the production database. The blast radius is the union of everything root can do, which is everything.
The operational tax compounds. Key rotation becomes a cross-team event. Revocation means touching authorized_keys on every destination. Audit is a shell history file that someone can edit or truncate. If you have contractors, CI runners, or ephemeral workers, you lose track of which key is still valid. And because the key is root, every command looks the same in logs: ssh root@primary <command>. There is no intent, no job name, no policy.
The alternative is not 'no SSH.' The alternative is ssh least privilege with a command gate. A dedicated key that can invoke exactly one entrypoint. That entrypoint accepts command names from a whitelist. The whitelist runs under sudo with an audit log. If Bailian is compromised, the attacker gets the menu, not the kitchen.
deploy_gate.sh: Command Names, Not Raw Bash
Tacavar's answer is deploy_gate.sh, a whitelist dispatcher on the primary droplet. Bailian holds a dedicated deploy key. In primary's authorized_keys, that key is forced to run deploy_gate.sh and nothing else. No shell, no port forwarding, no agent forwarding, no pty. When Bailian connects, it does not send bash -c '...'. It sends a command name and arguments.
The dispatcher parses the request as data. The first token must match an entry in an explicit allowlist. deploy, instagram_post, domain_pending, pip_install_requests, and so on. If the name is not on the list, the gate exits non-zero and logs the rejection. There is no eval. There is no fallback to /bin/sh. There is no wildcard that expands into arbitrary execution. Arguments are validated against per-command rules: expected flags, expected types, expected ranges, expected IDs.
This is the core of the Tacavar deploy gate. It turns cross-server RPC into a typed interface. Bailian can ask primary to 'post this approved Instagram reel' or 'check pending domains' or 'install this pinned pip package from the internal index.' It cannot ask primary to 'run this script I just wrote to /tmp.' That distinction is the difference between a deployment system and a shared root key.
Running Each Whitelisted Command Under sudo With an Audit Log
The gate itself does not run as root. It runs as a low-privilege deploy user. Each whitelisted command maps to a specific sudoers rule. deploy may run /usr/local/bin/tacavar-instagram-post as www-data, for example, with no password and no shell escape. deploy may run the deploy script as app, not as root. pip_install_* maps to an installer wrapper that only reads from an approved package list. The sudoers file is narrow by construction, because every entry is a concrete path.
Every call is written to an append-only audit log. Not shell history. Not syslog lines that scroll away. A structured record with timestamp, caller identity, command name, validated arguments, exit code, duration, and a hash of the payload. For commands that produce output, the gate stores a bounded stdout/stderr artifact and logs its location. For commands that mutate production, the log includes the pre-state and post-state references. Audit logging devops usually gets treated as a compliance tax. Here it is the mechanism that makes the gate debuggable. When a deploy runs at 2 a.m., you can answer who triggered it, which commit, which arguments, and whether it succeeded. When a command is rejected, you see exactly which name missed the whitelist.
The audit log also makes abuse visible in real time. If Bailian starts calling domain_pending a thousand times a minute, that is an alert. If a command fails validation repeatedly, that is an alert. The gate does not need a full SIEM to be useful. It needs a predictable, tamper-resistant record that an operator can query while incidents are live.
Adding New Commands Explicitly: instagram_post, domain_pending, pip_install
The whitelist does not grow by accident. Adding a command is a deliberate commit and a deliberate deploy. When Tacavar added Instagram posting, instagram_post was added to the dispatcher, given argument validation, mapped to a sudoers entry, and tested with a dry-run mode before Bailian could call it for real. When domain verification needed a queue check, domain_pending was added the same way. When internal services needed package installs, pip_install_* was added with a constrained package pattern and an approved-index check.
That friction is a feature. It forces the question: does Bailian actually need this capability, or does it need a narrower capability? If Bailian needs to trigger a video brief, it does not need write_file. It needs video_brief_create with a template ID and a target path. If it needs to install a package, it does not need apt-get. It needs pip_install_requests with a package name from the internal registry. Every new command is a chance to reduce the interface, not expand it.
Explicit addition also prevents shadow jobs. There is no path where a developer drops a script on primary and calls it from Bailian by guessing the name. The name must exist in the gate's allowlist. The allowlist is versioned. The sudoers rule is versioned. The audit log proves which version served the request. That is a level of traceability that shared root keys cannot match.
Least Privilege That Doubles as Documentation
The whitelist is not just a security control. It is the runbook. If you want to know what Bailian can do to primary, you read the dispatcher's allowlist. It is the complete, current list of cross-server capabilities. New engineers do not need to reverse-engineer shell scripts or ask around. They read the command names and the argument schemas. They see that instagram_post exists, that domain_pending exists, that pip_install_* is constrained. They see what is impossible by omission.
That documentation stays honest because it is executable. A command that is not in the whitelist cannot run. A command that is in the whitelist has a sudoers rule and an audit trail. There is no drift between the diagram and the system. For a founder/operator, this is the real payoff: you get most of the power of shared SSH with a fraction of the blast radius, and you get a live map of your production automation.
Tacavar's deploy gate is not a product you install from a marketplace. It is a pattern: dedicated key, forced command, whitelist dispatcher, sudo-scoped execution, structured audit log. It fits any two-droplet topology where one service needs to trigger jobs on another. The implementation is small enough to review in an afternoon. The security property is large enough to change how you think about cross-server RPC security.
Review Tacavar's least-privilege deployment capabilities at tacavar.com/platform.