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.
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 network sees the relay’s address, the server name your device asks for, when you connect and how much you send.
- The relay sees the same, plus your IP address. For
app.andapi.the relay reads the server name and copies the encrypted bytes. The relay holds no key for your session with the model server. - The network between the relay and the model server sees two servers connected, when, and how much they send. The tunnel’s TLS hides the server names and your session’s handshake from it.
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, settingPAI_KEYSTORE=filekeeps 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
- Sees
- The IP address and timing of every connection: account requests, vault requests, and the encrypted connections to
app.andapi.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.andapi.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.andapi., 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 forapp.andapi., 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.andapi., 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
- 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.
- 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.
- Stripe Checkout takes the card, and Stripe’s webhook marks the plan paid. The relay stores the subscription ID, not the email.
Day passes
- The device blinds a random value and sends it to the relay, in a request signed with its device key.
- 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.
- The device removes the blinding. The relay can’t recognize the result.
- 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
- 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. - 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. - 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
- 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.
- From then on, the vault is read and written with the vault secret alone. Nothing about the account is sent.
- Chats are encrypted with a random data key, which the key-encryption key locks, and padded to 64 KB or more.
- 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.
- A vault holds up to 100 MB. A vault nobody opens for 365 days is deleted.
Connecting a coding harness
- Connect a device in the web app shows a pairing code: 8 characters, one use, 10 minutes.
pai pair <code>makes an Ed25519 device key in the OS keychain. The device can only collect day passes.pai setup <harness>points the coding harness at pai’s local proxy, withpai tokenas its key command. For Claude Code, it also sets the switches that keep Claude Code from sending traffic to Anthropic.- 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
- Account > Make a new key, on any logged-in browser, makes and shows a new account key. The old key works until step 4.
- The browser re-locks the same data key with a key-encryption key derived from the new account key.
- 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.
- The browser tells the relay the new login hash. Only the new key logs in from then on.
- 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.
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
- Once a day, the model server makes a new Ed25519 key.
- 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.
- The relay checks both signatures, saves the new public key, and keeps it with the one before. Older keys stop working.
- 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:
- The TLS check. Let’s Encrypt connects to
app.andapi.asking for the protocolacme-tls/1. The relay passes that connection through the tunnel like any other. The model server’s gateway hands connections that offer onlyacme-tls/1to its certificate client, which answers for the model server’s Let’s Encrypt account. Nobody holds a DNS token. - CAA records on
app.andapi.allow only Let’s Encrypt, only the model server’s account, and only the TLS check (validationmethods=tls-alpn-01). They allow no wildcard certificates.relay.has its own CAA record for the relay’s account, by web check. - DNSSEC on the domain, so the CAA records can’t be forged.
- A daily certificate-log check on the model server looks up
app.andapi.in the public certificate logs and flags any certificate whose key isn’t DawonAI’s. Each renewal reuses the same key so the check can tell. - pai pins the key. pai refuses even a valid certificate with another key. Browsers can’t pin, so the web app relies on the rules above.
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.
| Adversary | Can learn or do | Can’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 |
|
|
| Someone controlling the model server |
|
|
| 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.
- The model server reads what you send. The model server has to, to answer. Requests and answers stay in its memory (and, until swap is off, possibly its swap file), so someone controlling the model server while it runs could read them. The day pass doesn’t hide content: anything identifying in a prompt identifies you.
- Requests from one day share a day pass, so the model server can tell they came from the same device. Day passes from different days can’t be linked through the day pass itself.
- Saved chats are one long-lived name. The vault ID never changes, and the web app syncs every 5 minutes while open. Someone watching the relay can tie a web user’s sessions across days through the vault ID, even over Tor, since chats from the same tab share the connection’s exit address. They still can’t read the chats.
- Few users means patterns stand out. The plan, the coding harness’s TLS fingerprint, working hours and request sizes can single someone out while there are few users, whatever their IP address.
- The relay sees IP addresses for account traffic, vault traffic and the encrypted API connections it forwards. The relay writes none of them down, but someone watching it live could link an account to its vault, or to its requests, by the shared IP address and timing. Use Tor or a VPN.
- Constant sync still shows when the web app is open, though not when you chat. It also shows:
- when a chat over 64 KB changes, because it goes up as overflow
- when a new chat is first saved, as a new blob ID
- each open tab, since every tab runs its own timer
- Opening and moving a vault can be timed. The vault credential, the new vault and its first upload arrive close together, and changing the account key copies the whole vault at once. Someone watching the relay live could link that vault to the account.
- The browser holds the vault secret and the data key. Anyone who copies the browser’s storage can download and delete the encrypted saved chats, and could decrypt them. Removing the browser stops its day passes but not that. Make a new account key after losing a device.
- The first day pass is collected on use. On a device’s first day, or a day after it was off at the random time, the device collects the day pass when it’s needed, so the collection time lines up with use.
- DawonAI serves the web app’s code, so a tampered release could steal keys. DawonAI publishes a hash of every release and serves no third-party scripts. Verify the app shows how to check.
- Card payments identify you to Stripe and link your account to you. They don’t link your account to your requests.
- Stripe isn’t a merchant of record, so DawonAI handles sales tax and VAT itself.
- Model server hardening isn’t finished. Swap is still on, journald still writes to disk, the disk isn’t encrypted, and the containers aren’t yet confined with AppArmor.
- The certificate rules aren’t in place yet. Until the CAA records and DNSSEC are set, someone controlling the relay could get a certificate for
app.andapi.by the usual check, and read or change what the web app sends. pai’s pin refuses such a certificate. Once the rules are in place, two gaps remain: a certificate authority that ignores CAA could still issue one, which the log check finds only afterwards, and whoever controls the domain’s DNS could change the records. - The relay’s backups are DigitalOcean’s. DigitalOcean keeps a daily backup of the whole relay, including the account database and the signing keys, and can read it.
- The model server sees whether a day pass is adult, as it sees the plan. Accounts with adult content on are few, so their requests stand out more.
- The relay could hand one account its own signing key to recognize that account’s day passes. The model server accepts one key per day and plan, so those day passes would fail there. Clients don’t yet compare the key they got with the model server’s.
Planned.
Before launch:
- CAA records and DNSSEC for the domain, and the daily certificate-log check on the model server.
- The first pai release for the production servers, signed with the release key and pinned to the model server’s key and an offline spare.
Decided, to be built after launch:
- Per-device and per-chat keys. The data key would be wrapped to each device’s own key, every chat would get its own key, and the data key would rotate when a device is removed. The account key would be used only to add a device or recover. Removing a device would then be enough to cut it off.
- The rest of the model server hardening: swap off, journald in memory only, LUKS with a passphrase entered by hand after each boot, and AppArmor for the containers.
Not in V1, and considered for later:
- Bitcoin payments through a self-hosted BTCPay Server
- approving new devices
- device keys stored in hardware
- newer credentials, now an IETF draft, that can limit streams without a daily key