Use promo code BETATEST1 for full access
seekrit
← all posts

Your GPU node lives for an hour. Your API key lives for a year.

Patterns

SF Compute sells H100 time on a market. You buy a contract for a few hours, nodes come up in about five minutes, and when the contract ends they are gone. It's a good deal and it's a genuinely different machine model from a server you keep — which means the habits you carry over from the server you keep are wrong in a specific, expensive way.

Here is the shape of the problem. A rented node has exactly one seam where you get to put something on it before it boots:

sf buy -d '1h' -t h100v

and, attached to that order, a cloud-init startup script. SF Compute requires one anyway — it's how your SSH key gets onto the box. So everyone writes one, and the obvious thing to do with a file that runs as root on a fresh machine is to put the machine's credentials in it.

That obvious thing is the mistake, and the reason is a lifetime mismatch.

The node is disposable. The script isn't.

The node dies in an hour. The startup script does not die in an hour. It is stored with the order, and it is replayed onto every node that order creates. So a HF_TOKEN you pasted in at 2pm on a Tuesday is now:

  • sitting in plaintext in a file held by a third party, for the life of the order rather than the life of the node;
  • identical on all eight nodes, and on any that come up later;
  • unrotatable without buying again, because the script is fixed at purchase;
  • unaudited, because nothing ever fetched it — it was already there.

Rotate that key next quarter and the fleet is stale, with no way to fix it in place. Leak it and you cannot tell which node leaked it, because all eight had the same copy and none of them left a record of reading it.

Meanwhile the actual machine — the expensive, powerful, internet-connected part — was gone forty minutes after you stopped thinking about it. The ephemeral thing was the one you worried about. The permanent thing was the file.

One credential, not twenty

A training node is not a one-secret machine. It wants a Hugging Face token for weights, a W&B key for logging, object-storage credentials for checkpoints, and frequently a model-provider key because the eval at the end of the run calls an API. That's five, and the startup-script approach puts five plaintext values in that file.

The fix is to make the script carry a bootstrap credential and fetch the rest. Two things go in, and one of them is the SSH key you had to add anyway:

#!/bin/bash
set -euo pipefail

mkdir -p /root/.ssh
echo "ssh-ed25519 AAAA… you@laptop" >> /root/.ssh/authorized_keys
chmod 700 /root/.ssh && chmod 600 /root/.ssh/authorized_keys

echo 'SEEKRIT_TOKEN=skt_XXXXXXXX_…' > /etc/seekrit.env
chmod 600 /etc/seekrit.env

curl -fsSL https://run.seekrit.dev/install.sh | sh

cd /workspace
seekrit-run --env-file /etc/seekrit.env --cache -- python train.py

seekrit-run is about two megabytes, statically linked, with TLS roots compiled in — no Node, no OpenSSL, no system CA bundle, which matters on a machine whose image you did not choose. It authenticates with the token, pulls the environment's ciphertext, decrypts it on the node, and execs your command with the five variables set. The seekrit API never sees a plaintext value; decryption happens on the GPU, the same way it happens in the browser.

Five credentials became one, and that one is bound to a single environment rather than being five different vendors' production keys.

Yes, the bootstrap token is still a credential

It is. Anyone who says otherwise is selling something. A token in a cloud-init script is a real secret in a file a third party stores, and the honest claim is narrower: it is one secret instead of five, it is scoped to one environment instead of spanning five vendors, and — this is the part that actually matters — it is revocable in one command, which the five never were.

seekrit token revoke skt_XXXXXXXX

Mint it per contract and revoke it at teardown. seekrit token list shows last-used time, so an order you forgot about is visible instead of silent. That is a cleanup story the pasted-keys version does not have at all: there is no command that un-pastes a Hugging Face token out of an order you placed in August.

The thing to never put in a startup script is an admin token. It's org-scoped and it can mint more tokens, which turns one leaked file into a permanent foothold.

If the job isn't your code, don't inject at all

Everything above assumes the thing on the GPU is your training script. Injection is fine there — it's code you wrote, and it needs the keys.

It stops being fine when the GPU is running an agent, an eval harness executing generated code, or a job someone else submitted. Anything that can call os.environ can exfiltrate what's in it, and a model that got talked into it will. There, run the proxy on the node and hand the workload a name instead of a key:

OPENAI_API_KEY='{{seekrit:OPENAI_API_KEY}}' \
  OPENAI_BASE_URL=http://127.0.0.1:8080/openai \
  python agent_eval.py

The real value is substituted on the way out to an allowlisted host, inside a process the job cannot read. Same node, same contract, nothing in the environment worth stealing.

The general version

This isn't really about SF Compute. It's about every platform where you rent a machine briefly and get one pre-boot hook to configure it — which is most of them now, and increasingly most of where GPU work happens.

The instinct carried over from long-lived servers is "the machine is permanent, so provisioning it is a one-time cost." Rented compute inverts that. The machine is the cheap, disposable half. The configuration artifact is the durable one, and it is durable somewhere you don't control.

So put as little in it as will still boot: the key to get in, and one credential that can be turned off from somewhere else.


The full setup — VM nodes, the Kubernetes path for h100i clusters where you're a namespaced tenant rather than an admin, and per-run config with a TTL that expires with the contract — is in the SF Compute guide.