Security model
A short, honest pass over what Naasson protects, what it doesn’t, and where the seams are.
What we sign
- Session JWTs — HS256 signed with
JWT_SIGNING_KEYheld in Lockbox. 24-hour TTL. Bound toSubject = user_id. - Agent tokens — random 32 bytes. We store only
sha256(token); the cleartext is shown to the user once on creation and never recoverable. The edge authenticates each agent dial-in by hashing the presented token and comparing. - Edge-handoff JWTs — 30-second TTL, single-use, signed by the
same key. Used to bounce a logged-in user from
api.cloud.naasson.comto a*.cloud.naasson.comFQDN with a fresh per-FQDN cookie. - Wildcard TLS —
*.cloud.naasson.comfrom Let’s Encrypt via ACME, auto-renewed by a cron on each edge node that pulls from Yandex Certificate Manager into Lockbox.
Trust boundaries
| You trust | We trust | Out of scope |
|---|---|---|
| Your own host | The cleartext payload of an agent token, exactly once | The hosts of other rental providers (Mode 3) |
| The browser session cookie scoped to one FQDN | YDB row integrity for tunnel_routes and tunnel_agents | The contents of WASM sandboxes (Mode 5, deferred) |
| The wildcard cert chain | A valid OAuth response from Yandex/Google/GitHub | Hosts running unknown rented workloads |
What stops cross-user impersonation
Two checks, deployed 2026-05-16:
POST /tunnel-agentsverifies thatworkspace.user_idmatches the caller’sclaims.subject. Without this, any logged-in user could mint an agent token for any workspace they happened to know the ID of. Returns 404 (not 403) on mismatch so workspace IDs stay unprobeable.POST /tunnel-routesverifies the same for both workspace and agent. Even when the caller owns both objects, the route’s workspace must match the agent’s workspace — preventing cross-workspace “I own a vanity name pointing at your service” attacks.
Cross-user attempts are warn-logged for forensic review.
What stops a malicious workload reaching your host
The agent forwards bytes to localhost:<target_port>. It does not:
- exec shell commands from edge frames;
- write to the filesystem;
- inspect or modify the payload;
- access more than the one port you named when creating the route.
The systemd unit drops to a dedicated naasson system user, with
ProtectSystem=strict, ProtectHome=true, NoNewPrivileges=true,
and MemoryDenyWriteExecute=true. The agent has no read access to
your home directory or any other user’s files.
What we don’t read
- The bodies of requests through the edge. TCP passthrough; we see the TLS-encrypted bytes only as bytes.
- Your workspace state. The blob
state_jsonon a workspace row is treated as opaque. - Your cloud provider credentials. When you connect a Yandex/AWS/ GCP account to a workspace, the API tokens flow through the worker pipeline and into a per-deploy ephemeral env — never written to YDB or to logs.
What’s deferred
- WASM sandbox audit-by-hash for Mode 5 batch — we sign the manifest hash and a host that runs the workload commits to that hash, but the runtime enforcement story is still being designed.
- mTLS client certs in the browser (
auth_mode=cookie+mtls) — drafted in R5.2 but not yet wired. - HSM-backed JWT signing. Today the key is in Lockbox, which is itself encrypted; the next step is to move the signing operation inside a hardware module.
How to report
Security issues go to security@naasson.com. Please don’t open public
issues for things that affect cross-user data — give us a window
between report and disclosure. We don’t run a bug bounty yet, but we
will publicly thank you.