All documents · Security

Security challenge — CryptoBook pivot

Role: adversarial security (not market, not legal except where a design is a credential crime waiting to happen).
Date: 18 September 2026.
Box we actually have: one public Ubuntu 24.04 VPS (vmi3003269, 173.249.19.205). App must bind 127.0.0.1, public only nginx 80/443. Postgres/Redis/Mongo on localhost. No HSM, no tenant KMS, no SOC2, SSH still a root-password-class login surface. We cannot honestly harden a vault of other people’s LLM keys, GitHub PATs, Slack tokens, or session cookies on this machine.

Ideas corpus: /root/cryptobook-next/ideas/ was empty after a 3-minute poll (06:24–06:27 UTC). Verdicts below are against BRIEF candidates A–E plus the naive implementations those one-liners always become. If product later pastes a BYOK proxy, a remote MCP that sees keys, or a “we log into X for you” agent, this document still kills it.

Kill tests (instant, non-negotiable):

  1. Users must hand LLM API keys (or cloud QPU keys, or GitHub/Slack/AWS tokens) to a random new SaaS, with no safer architecture.
  2. We proxy arbitrary third-party logins / hold session cookies (“browser agent in the cloud”).
  3. The product is a high-value credential honeypot we cannot harden on one VPS.
  4. We log secrets or prompt bodies by default.

Preferred pattern (the only pattern that survives on this VPS): local PEP sidecar / stdio MCP wrapper that never sees model keys; cloud is a policy compiler + hashed receipt ledger; prompt bodies off by default; no third-party login proxy.

Legend: KILL = do not build, even “encrypted at rest.” FIX = the wedge can live only if the architecture below replaces the naive one. SURVIVE = buildable now, but only with the architecture named in that section. There is no second architecture.


Threat model (this VPS, not a slide)

Assume attacker gets root or the Node app. That is in-scope; it is how VPS SaaS dies.

If we store… Blast radius on this box Verdict
Customer OpenAI/Anthropic/Google keys Attacker drains every org bill, exfiltrates fine-tunes, impersonates the customer to the model provider Uninsurable honeypot. KILL
GitHub PATs / Slack / AWS / “tool” secrets Source, prod, chat, cloud. One RCE = industry news KILL
Browser cookies / “login as the user” CFAA-adjacent, account takeover factory KILL
Prompt bodies / tool args by default Trade secrets, passwords pasted into agents, customer PII KILL default; opt-in local-only only
Our own scoped project tokens + Ed25519 pubkeys + policy text + receipt hashes Integrity nuisance, metadata leak, not a key vault Acceptable
Stripe customer IDs (no PAN) + emails Normal SaaS, still minimize Acceptable

“We encrypt keys with a server-side KEK in env” is security theater on a single VPS: the app must decrypt to proxy, so memory, swap, core dumps, DEBUG logs, and the stolen .env all have the KEK. Do not ship that.

nginx access logs, PM2 logs, and “just while we debug” request dumps will store Authorization and prompt JSON unless the product refuses to receive them. Architecture, not a logging config, is the control.


A. Permit gateway for agent tools/MCP — “one URL drop-in”

Naive product (what “one URL drop-in” means in 2026): hosted Streamable-HTTP MCP or reverse proxy. User pastes https://cryptobook.space/p/… into Cursor/Claude. We see every tool call. Spend cap on “the user’s own keys” means we terminate TLS to OpenAI with their key. This is Helicone/LiteLLM with a new coat of paint.

Verdict: KILL the hosted drop-in proxy.
Verdict: FIX the wedge (English rules, allow/deny/ask, spend cap, signed receipts) into the architecture in §Canonical.

Why the proxy dies:

Not an acceptable FIX: envelope encryption, “keys in the browser,” “we are just a proxy, we don’t store them” (they are in RAM on every call), customer-managed keys that we still use as the TLS client.

Acceptable FIX: do not terminate model or tool credentials. Cloud = policy compiler + PDP that receives non-secret decision requests (tool name, arg hash, estimated USD, policy id). PEP = local sidecar. See §Canonical.


B. Repo agent-permit.yaml runtime — Dependabot-like, “hosted enforcement”

Naive product: GitHub App plus our servers run or proxy the agent in CI with OPENAI_API_KEY and GITHUB_TOKEN injected into our runtime.

Verdict: KILL hosted enforcement that executes agents or holds CI secrets.
Verdict: FIX to “Dependabot for the policy file” + local/CI PEP.

Allowed:

Forbidden:

Viral YAML in the repo is fine. Enforcement affinity is the customer’s process, not ours.


C. Freelance / client agent receipts — “prove the agent did the work”

Naive product: full transcript + tool I/O hosted so the client can “see what the bot did.” That is a confidential-work dump on a box we cannot isolate per tenant. Freelancers will paste API keys, customer data, and credentials into prompts. Default logging = we become their lawyer’s discovery archive and the attacker’s corpus.

Verdict: FIX (hashed / user-held preimage receipts). Not KILL if we never host plaintext work product.

Only acceptable receipt:

Do not build “session replay for agents.” That is observability-as-honeypot.


D. Quantum circuit classroom

Verdict: SURVIVE (security only), with a hard fence.
This is not a credential product if it is a browser/WASM or localhost simulator. No IBM/AWS/IonQ/Azure Quantum keys. No “we’ll run this on real hardware, paste your IBM token.” That token is as hot as an LLM key and we have no QPU; collecting it is both a honeypot and a lie.

Fence:

(Demand/monetization may still kill this elsewhere. Security does not.)


E. “Anything more specific” (none filed)

Verdict: KILL the unstated cluster that idea agents always reach for in this market:

Unstated idea Why it dies
Hosted BYOK LLM gateway / “OpenAI proxy with budgets” Key honeypot; me-too Helicone
Cloud Computer-Use / “agent logs into Notion/GitHub/bank” Third-party login proxy + cookie vault + CFAA heat
Agent observability that stores prompts (Langfuse clone) Default secret log; crowded; we cannot isolate tenant traces
“Agent identity provider” holding long-lived tool OAuth refresh tokens Okta-shaped honeypot we cannot harden
Quantum job broker (submit circuits with customer QPU keys) Key custody + hardware we do not have

Canonical product that patches the kills — SURVIVE

Name the thing honestly: a policy compiler + local permit sidecar (PEP) + hashed receipt ledger. Not a proxy. Not a key wallet. Not an observability lake.

This is BRIEF A+B+C with the poison cut out. This is the only security architecture allowed if the company builds in the agent-permit space.

Split

Piece Where it runs What it is allowed to see
Policy compiler Our VPS English rules → signed policy bundle. No keys, no prompts.
PDP (optional, thin) Our VPS tool_name, args_hash, usd_estimate, policy_id, sidecar auth. Answer: allow / deny / ask.
PEP sidecar / stdio MCP wrapper Customer machine or their CI runner Tool calls, local spend counters, local LLM HTTP if they enable a local spend wrap. Never forwards model keys.
Receipt ledger Our VPS Merkle roots, signatures, policy hashes, aggregates.
Playground Our VPS + browser Sample traces, fake tools (calc, weather). Zero user secrets for the 60-second path.

Rules that are product law, not a backlog ticket

  1. LLM API keys never leave the user’s runtime. We have no column, no header, no “paste your OpenAI key to try spend caps.” Spend caps: local wrapper around the provider HTTP client on their box, or provider usage APIs called from the sidecar. Cloud gets signed {tokens, usd_estimate, model}, not the key and not the prompt.
  2. MCP wrapper does not need the model key (the host already has it). Wrap tools, not the chat API, unless the spend wrap is local.
  3. No remote MCP as the default path if that MCP would see real tools or real credentials. Demo remote MCP may only expose toy tools we own with no customer auth.
  4. No third-party login proxy. We may use GitHub OAuth as login to CryptoBook. We may ship a GitHub App for our checks. We do not OAuth-on-behalf into the customer’s Slack/Google/GitHub as an agent. Sidecar talks to local MCP servers that already hold those tokens.
  5. Prompt bodies and raw tool arguments are not stored, not uploaded, not in nginx logs. Default is hash-only. An --upload-bodies flag is a footgun: do not implement it in v1. If ever, it is local disk, not our API.
  6. Ask/approve is local. Pending payloads stay in the sidecar. Cloud may notify “tool repo.force_push wants approval” and record the signed decision. If the only pretty UI is our website, the payload would have to come to us — that UI is forbidden for real payloads. Local TUI / desktop notification / permit approve CLI.
  7. Receipt integrity: sidecar generates Ed25519 at init; user registers pubkey with the project. Stolen project token can post garbage but cannot forge a valid receipt. Verify on a public page with the pubkey. We log hashes, not preimages.
  8. Project tokens are ours, scoped to fetch_policy + post_receipt, rotatable, hashed at rest (store only a hash, like a password). They are not customer crown jewels, but treat them as credentials anyway.
  9. Do not log secrets by default: structured logs with explicit allowlists. Redact authorization, cookie, api_key, token, sk-, Bearer. Refuse request bodies on any public route that could be a mistaken proxy.
  10. Bind 127.0.0.1. No extra ports. Do not reuse LiveKit/TURN for this product.
  11. Honest copy: “We never take your model keys. Enforcement runs next to your agent. We store hashes, not chats.” If we cannot say that on the homepage, the architecture has already drifted to KILL.

60-second path that does not take keys

  1. Homepage: paste three English rules → compiled agent-permit.yaml + allow/deny on canned tool traces in the browser (WASM/JS evaluator).
  2. npx local sidecar, point Claude/Cursor at stdio MCP.
  3. Optional account to publish a policy and later verify a receipt. Account is not required to get the first deny.

That is easy start without becoming a key dropbox.

What a VPS breach then means (acceptable)

Attacker gets emails, policy text (internal tool names — disclose this), receipt hashes, Stripe customer IDs, hashed project tokens. They do not get OpenAI keys, GitHub PATs, prompts, or the ability to mint valid receipts for a sidecar they do not have the Ed25519 secret for. That is the difference between a dead company and a bad week.


Scoreboard

ID Idea Verdict Only acceptable architecture
A Hosted permit gateway / one-URL MCP or LLM proxy KILL
A′ English permit rules + spend cap + receipts, local PEP FIX → SURVIVE §Canonical: sidecar/MCP wrapper never sees model keys; cloud = compiler + hash ledger
B agent-permit.yaml hosted agent runtime KILL
B′ YAML in repo + GitHub checks + customer-runner PEP FIX → SURVIVE App installation token only; secrets stay in their CI; hashed receipts
C Freelance receipts with hosted transcripts KILL default logging
C′ Shareable hashed receipts SURVIVE Merkle + local Ed25519; no bodies on our disk
D Quantum classroom (simulator) SURVIVE No QPU keys ever
D′ Quantum classroom + “paste IBM/Braket key” KILL
E BYOK proxy, cloud browser-login agent, prompt lake, agent IdP vault KILL
F Canonical Permit (compiler + local sidecar + hash ledger) SURVIVE §Canonical only

Ship F. Anything that needs the user’s sk- on 173.249.19.205 is a honeypot with a landing page.