How DawonAI protects you.

Describes the V1 design as of 24 October 2026. Where a step isn’t done yet, this page says so.

DawonAI splits what it knows between two servers. The relay knows which account paid. The model server knows what was asked. This page lists what each part holds, sees and stores, and what someone controlling it could learn. Security questions has the short version.

The web app and pai connect to the relay over HTTPS. The relay passes app and api connections, still encrypted, through a tunnel that the model server opens. Stripe sends payment webhooks to the relay. Cloudflare serves the landing pages and carries the free demo to the model server. DawonAI web app in your browser Coding harness local key pai local proxy, checks the pin HTTPS HTTPS Relay a VPS Accounts and devices Day pass signing Encrypted saved chats Passes app. and api. connections through unread Tunnel, TLS 1.3 opened by the model server Model server no inbound ports TLS for app. and api. ends here Serves the web app Checks day passes Safety screen Qwen 3.8 27B webhook Stripe card and email free demo Cloudflare landing pages, free demo The web app and pai connect to the relay over HTTPS. The relay passes app and api connections, still encrypted, through a tunnel that the model server opens. Stripe sends payment webhooks to the relay. Cloudflare serves the landing pages and carries the free demo to the model server. Web app in your browser pai for coding harnesses HTTPS Relay Accounts and devices Day pass signing Encrypted saved chats Passes app. and api. through unread webhook Stripe card and email Tunnel, TLS 1.3, opened by the model server Model server TLS for app. and api. ends here Serves the web app Checks day passes Safety screen and Qwen 3.8 27B No inbound ports free demo Cloudflare landing pages, free demo
Blue outlines are DawonAI’s servers. Dashed outlines are other companies. Each arrow points away from the side that opens the connection.

What each connection hides.

Two TLS sessions carry your traffic. Your own runs from your device to the server that answers: the model server for app. and api., and the relay for account and saved-chat requests. Between the relay and the model server, the tunnel has a TLS 1.3 session of its own, which carries yours inside it.

Your TLS session for app. and api. runs from your device to the model server, through the relay unread and inside the tunnel's own TLS 1.3 session. Your TLS session for relay. ends at the relay. Your device web app or pai Relay a VPS Model server the GPUs To app. and api. your TLS starts passed through unread reads the server name only your TLS ends here the internet tunnel: TLS 1.3 relay certificate pinned model server signs in To relay. your TLS starts the internet your TLS ends here: accounts and saved chats
Blue lines are your TLS sessions, from the side that starts each one to the side that ends it. The dark band is the tunnel’s own TLS 1.3 session.

The parts.

There are six parts. DawonAI runs the relay and the model server, and ships the web app and pai.

The DawonAI web app

Runs in your browser. The model server serves its files.

Holds
A device key, which the browser can’t export. The key-encryption key, the chats’ data key, and the vault ID and vault secret, all derived from or locked by your account key. Day passes. The web app never stores the account key itself.
Sees
Your chats in the clear, on your device.
Stores
The keys above, your downloaded chats and edits waiting to sync, in the browser’s storage.
If copied or stolen
A logged-in browser has full account access: it can read saved chats, use your plan, make a new account key and delete everything. Someone with a copy of its storage could download and decrypt your saved chats, even after you remove the device. Remove the device and make a new account key.

pai

Runs on your computer, for coding harnesses. macOS, Windows and Linux.

Holds
A device key, today’s and tomorrow’s day passes, and a local key, in the OS keychain. A config file with the relay and API addresses, the device ID and the account ID. Never the account key or saved chats.
Does
Runs a local proxy at http://127.0.0.1:47821. Your coding harness sends requests there with the local key, which only works on this computer. The proxy:
  • swaps the local key for the day pass, so the coding harness never sees the day pass
  • connects to the model server over TLS and checks its public key against the pins built into the pai release: the model server’s key and a spare kept offline. Any other key gets an error and nothing is sent.
  • answers only on 127.0.0.1, only with the local key, and refuses requests from web pages
  • collects tomorrow’s day pass once a day, at a random time
If stolen
Someone with the computer can use your plan until you remove the device in Devices. They get no saved chats and no account key. A pai device that collects no day pass for 30 days is removed automatically.
Not done yet
No release of pai for DawonAI’s production servers is published yet. The spare key it will pin is still on the model server, waiting to be moved offline. pai’s binaries aren’t code-signed. The installers check each download against SHA256SUMS. On Linux without a Secret Service, setting PAI_KEYSTORE=file keeps the secrets in a file with mode 0600 and no encryption.

The relay

A VPS. Serves relay., and passes app. and api. through.

Holds
The day pass and vault credential signing keys. The TLS keys for relay. and for the tunnel port. The model server’s tunnel public keys: the current one and the one before.
Stores
An SQLite database:
  • accounts: the account ID, the SHA-256 of a login key derived from the account key, the plan, status and paid-until date, the inactivity setting and deletion date, Stripe’s subscription ID, and a count of vault credentials for the current 30-day block
  • devices: ID, name, kind, the day each was added and the day each last collected a day pass
  • vaults, with no account ID: the vault ID, the SHA-256 of the vault secret, the day each was last opened, and the encrypted blobs with their IDs and sizes, stored in vault ID order rather than creation order
  • spent vault credentials, until they would have expired
The relay stores Stripe’s subscription ID, not your email or card. DigitalOcean, which hosts the relay, keeps a daily backup of the whole server.
Sees
The IP address and timing of every connection: account requests, vault requests, and the encrypted connections to app. and api. that it forwards. It holds login rate limits per IP address in memory. The relay writes no IP addresses or request logs.
Doesn’t see
Request content and the web app’s files: TLS for app. and api. ends on the model server. Saved chats in the clear.
If compromised
Someone controlling the relay while it runs could link an account to its vault and its requests by IP address and timing, and tie a web user’s sessions together through the vault ID. They could try to get a certificate for app. and api., which the certificate rules are there to stop.
If seized
The database, and DigitalOcean’s backups of the server. No IP addresses, prompts, answers or readable chats.
Hardening
  • The firewall accepts web traffic on ports 80 and 443, with per-address limits on new and open connections. It accepts SSH only from one admin address, and the tunnel port only from the model server’s address.
  • SSH takes keys only. Deploys go through an upload-only account, locked into one folder, with no shell. An administrator installs an upload only after its checksums match.
  • The system journal is kept in memory, and syslog is off.
  • Security updates install automatically.
Not done yet
The relay’s own encrypted backup is off, so the only backups are DigitalOcean’s, which DigitalOcean holds and can read.

The model server

A ThinkStation with two GPUs and no inbound ports. It opens the tunnel to the relay itself.

Runs
The gateway, in rootless Podman with a read-only root filesystem, every capability dropped and no-new-privileges. The gateway ends TLS for app. and api., serves the web app’s files, checks day passes, counts streams, runs the safety screen and translates between APIs. Behind the gateway, SGLang serves Qwen 3.8 27B on each GPU, and NVIDIA’s Llama-3.1-Nemotron-Safety-Guard-8B as the safety screen.
Network
No inbound ports. The model server sits on its own network segment, cut off from other machines on the same network. Outbound connections are refused except to listed hosts, such as the relay and Let’s Encrypt.
Sees
Every request and answer in the clear, in memory. The day pass on each request, and so its plan and which requests share it. Never accounts, payments or the account key.
Holds in memory
Open streams per day pass, demo limits, an hourly count of safety refusals, and error records with exact times for one hour. At midnight UTC the day’s keys expire, along with everything the gateway held about them.
Stores
The model weights (read-only), the web app’s files, the TLS key for app. and api., its Let’s Encrypt account key, the tunnel key (sealed by the model server’s TPM, so the file can’t be used on another machine), and hourly error summaries. A summary records the endpoint, plan, coding harness family and a rounded request size, never content, IP addresses, day passes or accounts. Summaries are written after each hour ends and kept for 30 days. SGLang logs warnings only, so per-request timing isn’t written to disk.
If compromised
Someone controlling the model server while it runs could read requests and answers, and could serve a changed web app that sends keys away. The day pass still doesn’t tell them which account paid.
If seized
The files above. DawonAI’s software writes no prompts or answers to disk. Swap is still on, so until that’s done, memory can be paged to disk.
Not done yet
Turning swap off, keeping journald in memory only (it writes to disk today), full-disk encryption (LUKS) with a passphrase entered by hand after each boot, AppArmor confinement for the containers, and the daily certificate-log check.

Stripe

Stripe Checkout takes card payments.

Holds
Your card and email. Stripe sends payment events to the relay by webhook.
Links
The relay stores Stripe’s subscription ID with your account, so Stripe’s records and the relay’s together tie the account to you. Neither links the account to your requests.
Doesn’t see
Requests or chats. Stripe isn’t in their path.

Cloudflare

Serves the landing pages, including this one and the release hashes on Verify the app, and carries the free demo to the model server.

Sees
Visits to the landing pages, and the free demo. The protections on this page don’t cover the free demo.
Doesn’t see
The web app, pai’s connections or the relay. Cloudflare isn’t in their path, and the web app loads no Cloudflare script.

The flows.

Sign up and pay

  1. The browser solves a proof-of-work puzzle, which slows bulk signups. DawonAI doesn’t use Cloudflare Turnstile, which would load Cloudflare’s script into the page that holds your keys.
  2. The browser makes the account key: 24 characters, 120 bits. The web app won’t continue until it has shown you the key. From the account key, HKDF derives a login key, a key-encryption key, a vault ID and a vault secret. The relay stores the SHA-256 of the login key.
  3. Stripe Checkout takes the card, and Stripe’s webhook marks the plan paid. The relay stores the subscription ID, not the email.
HKDF derives four values from the account key: a login key, whose SHA-256 the relay stores; a vault ID, which the relay stores with no account ID; a vault secret, of which the relay stores a hash; and a key-encryption key, which never leaves your device. The key-encryption key locks a random data key, which encrypts the saved chats stored in the vault. Account key 24 characters, 120 bits you keep it HKDF, one label each Login key made when you log in Vault ID names your vault Vault secret opens your vault Key-encryption key never leaves your device Relay stores its SHA-256 and checks it at login Relay stores the vault ID with no account ID Relay stores a hash checked on each vault request locks Data key random, made on your device stored locked, in the vault encrypts Saved chats, in the vault encrypted, padded to 64 KB
Blue: what the relay stores. White: on your device. The relay never receives the account key or the key-encryption key.

Day passes

Your device blinds a random value and sends it to the relay, signed with its device key. The relay checks the plan and signs blind. Your device removes the blinding to get the day pass, which the relay never saw. Your device sends requests with the day pass to the model server, through the relay unread. The model server checks the signature and counts open streams in memory. Your device web app or pai Relay knows the account Model server sees the request Makes a random value and blinds it. blinded value signed with the device key Checks: plan paid, device registered, under 3 day passes for that day. Signs blind, with the key for that day and plan (RFC 9474). blind signature Removes the blinding. The result is the day pass. The relay never saw it. request with the day pass passed through unread Checks the signature with that day’s public key for the plan. Counts open streams in memory.
The relay sees which account asked for a signature, and when. The model server sees a valid signature for a day and plan. The day pass carries nothing that matches the two.
  1. The device blinds a random value and sends it to the relay, in a request signed with its device key.
  2. The relay checks the plan is paid through the start of that day, the device is registered, and the device has collected fewer than 3 day passes for that day. Then it signs blind, with the key for that day and plan: RSA blind signatures, RFC 9474, variant RSABSSA-SHA384-PSSZERO-Deterministic.
  3. The device removes the blinding. The relay can’t recognize the result.
  4. Each device collects tomorrow’s day pass at a random time today: pai’s proxy once a day, and the web app while open, never when a message is sent. The time of collection, which the relay sees with the account, says nothing about when the day pass is used. A device’s first day, or a day after it was off at the random time, collects on use.

A request

  1. The web app, or pai for a coding harness, sends the request to api. over TLS. pai first checks the model server’s key against its pins. The relay passes the bytes through the tunnel unread.
  2. The gateway checks the day pass against the public key for that day and plan, and counts the day pass’s open streams in memory: 1 on Chat and on the $3 one-day plan, 8 on Agent. Over the limit, it returns 429.
  3. The safety screen checks the newest message, on the same machine. SGLang answers. The gateway holds the request in memory until the answer ends, then drops it.

Saved chats

  1. Once the plan is paid and the account key is final, the browser gets a blind-signed vault credential, with its own signing key, at most 2 per account in each 30-day block. It opens the vault with the vault ID, a hash of the vault secret and that credential. The relay knows a paying account opened the vault, not which one, and refuses a credential it has seen.
  2. From then on, the vault is read and written with the vault secret alone. Nothing about the account is sent.
  3. Chats are encrypted with a random data key, which the key-encryption key locks, and padded to 64 KB or more.
  4. While the web app is open, it uploads one 64 KB blob when it opens and every 5 minutes after, whether or not anything changed. A chat larger than 64 KB goes up alongside as overflow. Deleting a chat overwrites it with filler. Nothing uploads while the page is hidden or closed.
  5. A vault holds up to 100 MB. A vault nobody opens for 365 days is deleted.

Connecting a coding harness

  1. Connect a device in the web app shows a pairing code: 8 characters, one use, 10 minutes.
  2. pai pair <code> makes an Ed25519 device key in the OS keychain. The device can only collect day passes.
  3. pai setup <harness> points the coding harness at pai’s local proxy, with pai token as its key command. For Claude Code, it also sets the switches that keep Claude Code from sending traffic to Anthropic.
  4. No client certificates: a long-lived one would identify the device to the model server on every connection, and a daily one would sit in the same keychain as the day pass it protects.

Changing the account key

  1. Account > Make a new key, on any logged-in browser, makes and shows a new account key. The old key works until step 4.
  2. The browser re-locks the same data key with a key-encryption key derived from the new account key.
  3. The new account key gives a new vault ID and secret. The browser opens a new vault with the account’s second vault credential of the 30-day block, and copies every encrypted chat into it.
  4. The browser tells the relay the new login hash. Only the new key logs in from then on.
  5. The browser deletes the old vault. Other browsers stay linked, and log in again with the new key to reach the moved vault.

If step 3 or 4 fails, the new vault is deleted and the old key keeps working. The data key doesn’t change, so a browser keeps any chats it already downloaded.

The tunnel.

The model server has no inbound ports. The model server opens the connections the relay uses to reach it, and keeps a pool of them open. Every one starts the same way.

The model server opens a TCP connection to the relay's tunnel port. The relay answers with TLS 1.3 and its tunnel certificate, which the model server checks against a pinned fingerprint. Inside, the relay sends 32 random bytes, and the model server signs them with its Ed25519 key. The connection then waits in a pool until the relay splices a visitor's connection onto it, unread. Model server opens every connection Relay listens on the tunnel port TCP to the tunnel port The relay’s firewall drops it unless it comes from the model server’s address. TLS 1.3, with the relay’s tunnel certificate The model server compares the certificate’s SHA-256 with its pinned fingerprint and hangs up on any other. 32 random bytes, new for every connection From here on, everything is inside the TLS session. Ed25519 signature of those bytes The relay checks it against the two public keys it trusts: the model server’s current key and the one before. The connection waits in the pool, ready for a visitor. A visitor’s connection to app. or api. Spliced in unread. The visitor’s TLS ends on the model server.
Each arrow points from the side that sends. The relay refuses plain TCP and TLS before 1.3.

When a visitor connects to app. or api., the relay reads the server name in the visitor’s first message, which TLS sends unencrypted, and splices the connection onto an idle tunnel connection. From then on the relay copies bytes both ways without reading them.

Daily key rotation

  1. Once a day, the model server makes a new Ed25519 key.
  2. Over a tunnel connection of its own, the model server sends the new public key, signed by the current key and by the new one.
  3. The relay checks both signatures, saves the new public key, and keeps it with the one before. Older keys stop working.
  4. The model server seals the new private key with its TPM and uses it from then on. If the model server fails after the relay saved the key, the relay still trusts the old one, so the next attempt works.

Where the tunnel keys are

  • The model server’s private key is sealed by the model server’s TPM on disk and unsealed into memory at boot. A copy of the disk can’t be used on another machine.
  • The relay holds only public keys for the model server, so a copy of the relay’s disk can’t connect as the model server.
  • The relay’s tunnel certificate is separate from its public certificate for relay.. The model server holds its SHA-256 fingerprint, which is public.

Certificates.

Controlling the relay means controlling the IP address that app. and api. point to. That’s enough to get a certificate by the usual web check. So the model server gets its certificate by a check that reaches it through the relay, and these rules refuse everyone else:

Let's Encrypt looks up the CAA records for app. and api., then connects to them asking for acme-tls/1. The relay passes the connection through the tunnel unread. The model server's gateway hands it to the certificate client, which answers for the model server's account. The CAA records allow only that account and only this check. DNS for app. and api. Address: the relay. CAA: Let’s Encrypt, the model server’s account, TLS check only. Let’s Encrypt checks control of app. and api. looks up CAA acme-tls/1 Relay reads the server name, passes it through unread A certificate for its own account fails CAA tunnel Model server Gateway sees acme-tls/1, hands it on ACME client answers for the model server
Dashed outlines are other companies. The relay could answer the same check, but only for an account of its own, which the CAA records refuse.

Not covered: a certificate authority that ignores CAA could still issue, and the log check finds that certificate only after it’s issued. Whoever controls the domain’s DNS could change all of these records.

Not done yet. The model server gets and renews its certificate by the TLS check today. The CAA records, DNSSEC and the certificate-log check aren’t in place, and no pai release with the pins is published. Until they are, someone controlling the relay could get a certificate for app. and api. by the usual check.

Threats.

What each adversary can and can’t learn or do, under the design above.

Scroll the table sideways to see every column.

AdversaryCan learn or doCan’t learn or do
Your network See that you connect to DawonAI, when, and how much you send. Read requests, answers, saved chats or account traffic, which go over TLS.
The network between the relay and the model server See that the two servers are connected, when, and how much they send. Read or change tunnel traffic, or pose as either end: the tunnel is TLS 1.3 to a pinned certificate, and the model server signs every connection.
Someone controlling the relay
  • See IP addresses and the timing of every connection.
  • Link an account to its vault and its requests by IP address and timing.
  • Tie a web user’s sessions across days through the vault ID, over Tor too.
  • See when the web app is open, when a large chat changes, when a new chat is first saved, and how many tabs are open.
  • Time the opening or moving of a vault, and link it to the account.
  • Read the account records, and copy the encrypted vaults.
  • Until the CAA records are set, get a certificate for app. and api., and read or change what the web app sends.
  • Read requests or answers: TLS ends on the model server.
  • Read saved chats.
  • Recognize a day pass it signed.
  • Get a certificate for app. or api., once the CAA records are set.
  • Connect to the tunnel as the model server: the relay holds only the model server’s public keys.
  • Pass pai’s key check with another key.
Someone controlling the model server
  • Read every request and answer while the model server runs.
  • Tell which requests share a day pass, and their plan.
  • Serve a changed web app that sends keys away.
  • Learn which account a day pass belongs to from the day pass.
  • See accounts, payments or emails.
Stripe Know who paid. With the relay’s records, tie the account to that person. See requests or chats, or link the account to its requests.
Someone with a logged-in browser Everything the account can do: read saved chats, use the plan, make a new account key, delete everything. A copy of the browser’s storage can download and decrypt saved chats until you make a new account key. Learn the account key itself, which the web app doesn’t store.
Someone with a computer running pai Use your plan until you remove the device. The current day pass works until midnight UTC. Read saved chats, get the account key, or manage the account.
A legal order Obtain what’s on disk: the relay’s account records and encrypted vaults, DigitalOcean’s backups of the relay, the model server’s error summaries, and Stripe’s records from Stripe. Live access to a server gives what the relay and model server rows list. Obtain prompts, answers or IP addresses from disk. DawonAI’s software writes none.

Known limits.

Planned.

Before launch:

Decided, to be built after launch:

Not in V1, and considered for later: