Secrets

Blocks often need credentials — a database connection string, an AI API key — that must not appear in the pipeline file, logs, or on disk. Spade delivers these through secrets: named values a block reads at run time with get_secret, resolved from a secure store and injected into the sandbox by the runtime.

How it works🔗

  1. The block author reads a secret by a logical name:

    from spade import run, get_secret
    
    def handler():
        dsn = get_secret("db")   # logical name — the credential the block needs
        # ... connect and query ...
  2. The pipeline binds that logical name to one of your stored secrets. Only the secret's name appears in the pipeline — never its value:

    - id: 019cf4bc-2222-7000-0000-000000000000
      name: db.query
      args:
        query: "SELECT * FROM parcels"
      secrets:
        db: prod-postgres-dsn
  3. At run time the runtime resolves the value — from the OS keychain for a local spade run, or from the cloud KMS for a cloud run — and injects it into the block. get_secret("db") returns the value.

The value only ever exists in the block process's memory; it is never written to disk.

get_secret in each language🔗

The function is the same across every runtime library — it takes the logical name and returns the value, and raises/returns an error if the secret was not provided.

LanguageCallReference
Pythonget_secret("db")Python handlers
Rget_secret("db")R handlers
TypeScriptgetSecret("db")TypeScript handlers
GoGetSecret("db")Go handlers
Rustget_secret("db")?Rust handlers

Setting secrets🔗

For local runs, store secrets in the OS keychain with the CLI:

spade secret set prod-postgres-dsn

See spade secret for the full command reference.

Local vs. cloud🔗

Local secrets (keychain) and cloud secrets (KMS) are independent namespaces, so the same pipeline can resolve a name to a development credential locally and a production credential in the cloud. A block that references a secret must find it in the store for wherever it runs.

How the cloud KMS protects secret values🔗

The cloud store is more than a database column: it's a small, dedicated key-management service (KMS), isolated from the rest of the platform so that a compromise elsewhere doesn't expose secret plaintext.

  • Encrypted at rest. Each secret is encrypted with its own per-secret key, which is itself wrapped by a master key held only inside the KMS process. A database breach alone yields ciphertext, not usable values.
  • No standing worker credential. A cloud worker never holds a blanket credential to your whole secret store. For each invocation, the scheduler mints a short-lived, invocation-scoped token authorizing only the specific secret names that invocation declared. A compromised worker can read only the secrets of jobs currently flowing through it — not your entire store.
  • Audited. Every write, delete, and resolve is logged (who, which secret name, when) for incident response.

None of this is visible from block code — get_secret behaves identically whether it's backed by this cloud machinery or the local OS keychain.

Network access is separate🔗

Reading a secret does not require network access. If a block also needs the network — for example to reach the database whose connection string it just read — its block.yaml must additionally declare network: true. The two are independent: get_secret works without network, and network access is surfaced separately so users can see which blocks reach out.