Security posture
What we hold, and what we refuse to.
Ratchet sits in front of your most consequential actions. The smaller its blast radius, the better it does that job — so it is designed to know as little as possible.
Data boundaries
What Ratchet stores
- A SHA-256 fingerprint of your payload — never the payload
- Effect type, idempotency key, state, attempt count, timestamps
- The result you explicitly choose to record for replay
- Optional
agent_id,run_id, and a smallrequest_summaryyou control - Declared costs, in micro-USD
- An HMAC of each API key secret, keyed by a server-side secret
What Ratchet never has
- Your vendor credentials, API keys, or tokens
- The content of the actions it gates
- Network access to your systems — the gate is inbound only
- The ability to perform, retry, proxy, or modify any side effect
- Custody of any funds, yours or your customers'
Because only a fingerprint is stored, Ratchet can tell that a key was reused with different arguments — a real class of bug — without ever being able to reconstruct those arguments.
Implemented controls
Each of these has a test in the suite. Nothing here is aspirational.
Identity and access
- Keys stored as HMAC-SHA256, peppered with a server secret — a database leak alone yields no usable key
- Constant-time comparison, run even for unknown prefixes so timing reveals nothing
- Least-privilege scopes; a gating agent needs only
effects:beginandeffects:report - Immediate revocation, and per-key daily spend ceilings
Containing a runaway agent
- A circuit breaker per effect type opens when one starts running far more often than its configured hourly ceiling — the failure spend limits cannot see, because the loop that sends five thousand emails costs about a dollar
- An open breaker raises the effect type to
require_approvalby default: the work waits for a human rather than the agent being killed - A workspace-wide emergency stop, with no cooldown — a control reached for in a panic must not quietly undo itself
- Thresholds come from stored policy and counts from the database; nothing in an agent's payload, summary, or result can raise a ceiling
- Breaker controls require an operator credential. An agent that could close its own breaker was never contained
Knowing when we have failed
- The worker expires leases on a timer. If it stops, effects stay
pendingfor ever and every retry is toldin_flight— silently. So it reports a heartbeat per loop GET /workerzreturns503when lease expiry has stopped running. It needs no credential, so you can point your own uptime monitor at it — and you should- Deliberately separate from the readiness probe: a stalled worker must never take the control plane offline, because the gate still works without it
- A worker whose loop wedges mid-query exits rather than sitting there half-working, so the platform replaces it
Tenant isolation
- Every query is scoped by workspace; there is no unscoped read path
- A cross-tenant lookup returns
404, never a hint that the record exists - A valid lease token from another workspace is still rejected
- The same idempotency key in two workspaces is two independent effects
Outbound requests (SSRF)
- Webhook destinations must be https, DNS hostnames, on port 80 or 443
- Private, loopback, link-local, CGNAT, and cloud-metadata ranges are blocked — IPv4, IPv6, and IPv4-mapped forms
- DNS is re-resolved on every attempt and the socket is pinned to the checked address, closing the rebinding window
- A host resolving to any private address is refused outright, not filtered
- Redirects are never followed; response bodies are capped and discarded
Integrity of state
- A database-level unique constraint is what enforces at-most-once — not application logic
- Fencing tokens: a stalled worker cannot overwrite a newer attempt
- Budget reservations lock every scope before validating any, so parallel workers cannot collectively exceed a ceiling
- Credit movements are an append-only ledger, deduplicated by event id
Prompt injection
- Agent-supplied text is treated strictly as data. Nothing in a payload, summary, or result is ever interpreted as an instruction by Ratchet
- Effect types are pattern-constrained; results are size-capped
- Decisions come from stored policy and database state — never from request content
- An injected payload cannot widen a key's scopes, raise a budget, or change a policy
Payments
- Card details are entered on the provider's own page and never reach Ratchet
- Credit is applied only on a signed webhook — never because a browser reached a success URL, which is not proof that payment settled
- Payment webhooks are verified by HMAC over the raw body, with a replay window and event-id deduplication
- Refunds and disputes reverse credit through a compensating ledger entry, capped at what was actually credited
- Crypto is non-custodial. No private key is held and no funds are taken into custody; payments go directly to an address the operator controls
- Crypto quotes are struck in USD, so a price move between quote and settlement cannot mint credit. Underpayment is never rounded up
Rollback safety
- A compensation is itself a gated effect, so a retried rollback cannot double-refund
- A group being unwound refuses new forward steps
- A step with no compensation is reported irreversible; the group settles
unwind_failedrather than claiming a clean rollback - An effect with an unknown outcome blocks planning around it
Transport and abuse
- Per-key rate limiting, so tenants cannot exhaust each other
- Same-origin CORS by default; credentials are never granted to a wildcard origin
- Unknown request fields are rejected rather than silently dropped
- Secrets redacted from logs; internal detail never crosses the error boundary
- Payment webhooks verified by HMAC over the raw body, with a replay window
Retention and deletion
Effect records expire on a per-effect-type schedule you configure, bounded by your plan. Expired records are deleted by the worker — a leased effect is never collected, so live work cannot disappear. Deleting a workspace cascades to its keys, effects, policies, ledger, webhooks, and audit trail.
Reporting a vulnerability
Security reports are welcome and will not be met with legal threats. Include reproduction steps and give a reasonable window before disclosure. Mail security@ratchetgate.com, the same address published in security.txt.
Nothing terminates TLS in front of the gate
Your connection to Ratchet ends at Ratchet. There is no reverse proxy, CDN or WAF in the path decrypting your API key and your payload on the way through. That is a deliberate choice with a real cost — we forgo the DDoS absorption and bot filtering a proxy would give us — and we make it because a service that promises to store only a fingerprint should not route the original past somebody else first.
If that ever changes, this paragraph changes with it, before the architecture does.
What you can check yourself
Three of these are machine-verifiable without asking us anything. The fourth is a self-certification, and is labelled as one.
Verifiable by you, independently
- Every release since 0.2.0 carries SLSA provenance. The npm
package is signed with an attestation naming the exact workflow run and commit
that built it, so you can prove the artifact came from this source and not from
someone's laptop.
npm audit signatureschecks it. The two 0.1.x releases predate the signed publish path and carry no attestation. - Every decision carries a signed receipt, hash-chained, with the
key id inside the signed body so it cannot be repointed. Public keys stay published
for ever at
/.well-known/ratchet-receipt-key, so a receipt from last quarter still verifies after a key rotation. - OpenSSF Scorecard runs weekly against the repository and is computed by OpenSSF, not by us — branch protection, pinned dependencies, dangerous workflow patterns, token permissions.
Self-certified, against a public checklist
OpenSSF Best Practices — silver, 100% of both the passing and silver criteria.
- It is a self-certification. Nobody from OpenSSF inspected this code; the project answers a public checklist and the answers are public, so you can read every claim and its justification and disagree with any of them.
- Five criteria are recorded as unmet on that page rather than argued into a yes: no DCO sign-off, unsigned git tags, one cryptographic algorithm family, English-only, and a bus factor of one.
- Gold is not achievable here and we are not pursuing it. It requires two unassociated contributors and independent review of half of all changes, which is a statement about how many people work on this, not about the code.
What we do not claim
No third-party audit has been performed. There is no SOC 2 report, no penetration test, and no compliance certification. The controls above are implemented and covered by automated tests; that is a different and lesser thing than independent verification, and we will not blur the two.