Security posture·May 2026·Last reviewed by JoeJoePro AI

Whitepaper · v1.0 · Authored by Joe

The honest
threat model.

Every architectural claim on this page links to a file and a line range. Every gap we haven't closed is named below. If you have a security requirement we don't meet, tell me and I'll fix it. Direct line at the bottom.

Skip to direct line Read threat model
$ tail -f audit.log0/6

01 · Architecture

Five trust edges. No shared anything across tenants.

Trust boundaries

BrowserTLS 1.3cagent-studioVercel · Next.jsPLATFORM_SECRETSTRIPE_WEBHOOK_SECRETPROVISIONER_TOKENFirestorelaunchpad_users/{uid}Stripewebhook → sig verifiedOpenRoutersub-key · plan-limit capPer-tenant machinelp-{product}-{uid8} · own IP · own volumeHermes/OpenClawTLSRBACHMACbearerprovision

Five outbound trust edges from cagent-studio. The dashed boundary on the per-tenant machine means no shared filesystem, no shared OpenRouter key, no shared OAuth tokens. Every solid edge is a credentialed call with its own auth scheme.

02 · Primitives

Auditable line by line.
Click the source field; the code is open.

Encryption at rest

Stored credentials (OpenRouter sub-keys, BYOK provider keys) are wrapped in AES-256-GCM before they ever hit Firestore. The userId is mixed into the AAD so a ciphertext stolen from one record cannot be replayed against another.

Algorithm
AES-256-GCM (authenticated)
IV
12-byte, random per record
AAD
userId (binds ciphertext to owner)
Key custody
HKDF from PLATFORM_SECRET
Storage
Firestore launchpad_users/{uid}.openrouterKey
Source
src/lib/launchpad/openrouter.ts:1-230

Per-tenant isolation

Each paying user gets their own isolated tenant: separate machine, separate IPv4/IPv6, separate volume. There is no shared compute pod or shared filesystem. A compromise of one tenant does not give an attacker any path into another.

Boundary
1 user → 1 isolated tenant namespace
Naming
lp-{product}-{uid8}-{ts}
Network
Own anycast IPv4 + IPv6 per app
Storage
Dedicated encrypted volume per tenant
Filesystem
No shared mounts across tenants
Source
src/lib/launchpad/fly.ts:1-277

Hard cap mechanism Plan limit means plan limit

We do not enforce the cap in our application layer. OpenRouter does, at the API gateway. The model literally cannot complete a token over budget; OpenRouter returns 402 and we surface that to the dashboard. The cap is therefore not a promise, it is a property of the system.

Enforced by
OpenRouter (not us)
Mechanism
Sub-key with limit_reset=monthly
BYOK exempt
include_byok_in_limit: false
Cap source
Mapped from active subscription plan
Bound
Hard spend stop at configured plan limit
Source
src/lib/launchpad/openrouter.ts:145-150

OAuth token custody

The strongest claim on this page: when you connect Claude / ChatGPT / Grok via OAuth, the tokens are written only to /workspace/.hermes/ on your encrypted volume. They never transit cagent-studio. They never land in Firestore. Even Joe cannot read them without a direct shell into your specific machine, a step that is logged in your dashboard's audit panel.

$ ls /workspace/.hermes
# token.json refresh.json session.lock
# readable only inside your tenant machine, encrypted at the volume layer

Payment surface

We are not in the cardholder-data path. Stripe Checkout is hosted; the only thing that crosses to us is the webhook, which we verify by signature and reject after a 5-minute freshness window.

Card processor
Stripe Checkout (hosted)
PAN exposure
Zero, never hits our servers
Webhook auth
HMAC-SHA256 + timestamp
Replay window
Stripe-default 5 minutes
Subscription state
Firestore-mirrored, source-of-truth = Stripe
Source
src/app/api/stripe/webhook/route.ts

Authentication

Passwordless by design. There is no password to phish. A 6-digit single-use code expires in 15 minutes. Sessions are Firebase JWTs that rotate automatically.

Primary
Email + 6-digit code (passwordless)
Code TTL
15 minutes, single-use
Session
Firebase Auth JWT, auto-rotated
Recovery
Re-verify email (no security questions)
MFA
Not yet (planned: TOTP, Q3)
Source
src/app/api/auth/send-verification/route.ts

03 · Threat model

Six scenarios.
Blast radius and mitigation, no hand-waving.

● OK handled · ● PARTIAL mitigated, gaps named · ● GAP not yet

ScenarioBlast radiusMitigationStatus
Compromised OpenRouter sub-keySpend bounded by that tenant's configured plan capOpenRouter-enforced cap; key rotation on plan change; revoke API availableok
Compromised user tenant machineOnly that user's data + tokensPer-tenant isolation boundary; no shared volumes; force-rebuild from clean imageok
Leaked PLATFORM_SECRETAll OpenRouter sub-keys re-decryptable; OAuth tokens unaffectedStored as Vercel env var only; rotation procedure: re-encrypt all sub-keys + invalidatepartial
Stripe webhook replayNone, timestamp window rejectsSignature + 5min freshness check via stripe.webhooks.constructEventok
Email account takeoverTrial-tier access only (no card on file by default)15-min code TTL; new device sign-in alerts (planned Q3)partial
Insider access (Joe)Firestore docs visible to admin; OAuth tokens NOT visible without per-machine direct shellOAuth tokens live only on the user's encrypted volume, never replicated. Joe cannot read them remotely.ok

★ Direct line · The differentiator

Most of these gaps are
a weekend of work.

SOC 2 audit-readiness, your DPA, SSO, MFA, longer log retention, on-prem images, HIPAA BAA, custom isolation, if you're paying me and you ask, I'll have it for you Monday.

This is the single biggest reason to deploy on clawd.run rather than a cohort of strangers behind a support queue. Email goes to my phone. I read it before I read Twitter.

joe@joepro.ai

Typical reply < 2 hours · weekdays · UTC-04

04 · What we explicitly don't do (yet)

No lies of omission.
The status column is the honest answer.

SOC 2 Type IINot pursued (audit cost > current ARR)
ISO 27001Not pursued
HIPAA BAAWill sign for paid customer with valid use case
GDPR DPAWill sign yours; no boilerplate ready
SSO / SAMLWill build for Team accounts ≥ 5 seats
24/7 SOCNo, incident response is Joe's phone
Bug bountyNo program; report to joe@joepro.ai, paid case-by-case
Log retention7 days on tenant machine, deleted on machine destroy
On-prem optionOpenClaw is open-source, self-host yourself

Anything marked "will sign" or "will build" is an honest commitment. See Direct line.

05 · Audit it yourself

Source files referenced above.

src/lib/launchpad/openrouter.ts

AES-GCM envelope, sub-key minting, plan-limit cap enforcement

src/lib/launchpad/fly.ts

Per-tenant machine provisioning, IP allocation, volume attach

src/lib/launchpad/provisioner.ts

Key resolution order: BYOK → sub-key → pooled

src/lib/launchpad/usage.ts

Firestore launchpad_users schema, hour caps, cycle reset

src/app/api/stripe/webhook/route.ts

Stripe webhook sig verification, subscription state transitions

src/app/api/auth/send-verification/route.ts

6-digit code, 15min TTL, single-use