|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8" /> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | + <title>atomic — Domain identity for AI agents</title> |
| 7 | + <meta name="description" content="One binary gives your AI agent a domain-bound keypair, signed requests, an encrypted vault, and a deposit box for receiving secrets. No cloud, no accounts." /> |
| 8 | + <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| 9 | + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> |
| 10 | + <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet" /> |
| 11 | + <link rel="stylesheet" href="style.css" /> |
| 12 | +</head> |
| 13 | +<body> |
| 14 | + |
| 15 | + <!-- Nav --> |
| 16 | + <header class="nav"> |
| 17 | + <nav class="nav-inner"> |
| 18 | + <a href="/" class="nav-brand">atomic</a> |
| 19 | + <div class="nav-links"> |
| 20 | + <a href="https://github.com/plotondev/atomic" class="nav-link">GitHub</a> |
| 21 | + <a href="https://github.com/plotondev/atomic#cli" class="nav-link">Docs</a> |
| 22 | + </div> |
| 23 | + </nav> |
| 24 | + </header> |
| 25 | + |
| 26 | + <div class="nav-spacer"></div> |
| 27 | + |
| 28 | + <main class="page"> |
| 29 | + |
| 30 | + <!-- Hero --> |
| 31 | + <h1 class="hero-heading">Domain identity for AI agents.</h1> |
| 32 | + |
| 33 | + <p class="hero-body"> |
| 34 | + Your agent gets an Ed25519 keypair bound to its domain, a public identity document at <code class="ic">/.well-known/agent.json</code>, an encrypted vault, and a deposit box for receiving secrets. One binary, ~4MB, runs on the same box as the agent. |
| 35 | + </p> |
| 36 | + |
| 37 | + <pre class="code-block"><code>curl -fsSL atomic.bond/install | sh |
| 38 | +atomic init --domain fin.acme.com</code></pre> |
| 39 | + |
| 40 | + <div class="cta-row"> |
| 41 | + <a href="https://github.com/plotondev/atomic/releases" class="cta-btn">Download</a> |
| 42 | + <a href="https://github.com/plotondev/atomic" class="cta-link">View source</a> |
| 43 | + </div> |
| 44 | + |
| 45 | + <!-- agent.json --> |
| 46 | + <hr class="section-rule" /> |
| 47 | + |
| 48 | + <h2 class="section-heading">agent.json</h2> |
| 49 | + <p class="section-body"> |
| 50 | + Every agent publishes its public key at a well-known URL. The domain is the identity. |
| 51 | + </p> |
| 52 | + |
| 53 | + <pre class="code-block"><code>$ curl https://fin.acme.com/.well-known/agent.json</code></pre> |
| 54 | + |
| 55 | + <pre class="code-block"><code>{ |
| 56 | + "v": 1, |
| 57 | + "id": "fin.acme.com", |
| 58 | + "name": "fin.acme.com", |
| 59 | + "public_key": "ed25519:m2UrN...", |
| 60 | + "status": "active", |
| 61 | + "deposit": "https://fin.acme.com/d/", |
| 62 | + "created_at": "2026-03-07T12:00:00Z" |
| 63 | +}</code></pre> |
| 64 | + |
| 65 | + <p class="section-body muted"> |
| 66 | + <code class="ic">GET /</code> redirects here. The public key is how other services verify this agent's signatures. |
| 67 | + </p> |
| 68 | + |
| 69 | + <!-- Deposit box --> |
| 70 | + <hr class="section-rule" /> |
| 71 | + |
| 72 | + <h2 class="section-heading">Deposit box</h2> |
| 73 | + <p class="section-body"> |
| 74 | + The agent needs an API key. Instead of pasting it into a <code class="ic">.env</code>: |
| 75 | + </p> |
| 76 | + |
| 77 | + <pre class="code-block"><code><span class="code-comment"># Generate a one-time deposit URL</span> |
| 78 | +$ atomic deposit-url --label stripe_key --expires 10m |
| 79 | +https://fin.acme.com/d/eyJsYWJlbCI6...Rk4 |
| 80 | + |
| 81 | +<span class="code-comment"># Whoever has the secret POSTs it</span> |
| 82 | +$ curl -X POST ".../d/eyJsYWJlbCI6...Rk4" -d "sk_live_abc123" |
| 83 | +{"status":"deposited","label":"stripe_key"} |
| 84 | + |
| 85 | +<span class="code-comment"># Agent reads it from the vault</span> |
| 86 | +$ atomic vault get stripe_key |
| 87 | +sk_live_abc123</code></pre> |
| 88 | + |
| 89 | + <p class="section-body"> |
| 90 | + The URL is Ed25519-signed, works exactly once (nonce-tracked), and caps out at 24 hours. The secret is AES-256-GCM encrypted before it hits disk. |
| 91 | + </p> |
| 92 | + |
| 93 | + <pre class="code-block"><code><span class="code-comment"># Use secrets at runtime — no .env files</span> |
| 94 | +curl -H "Authorization: Bearer $(atomic vault get stripe_key)" \ |
| 95 | + https://api.stripe.com/v1/charges |
| 96 | + |
| 97 | +export OPENAI_API_KEY=$(atomic vault get openai_key) |
| 98 | +python agent.py</code></pre> |
| 99 | + |
| 100 | + <!-- Magic links --> |
| 101 | + <hr class="section-rule" /> |
| 102 | + |
| 103 | + <h2 class="section-heading">Magic links</h2> |
| 104 | + <p class="section-body"> |
| 105 | + Domain verification, like DNS TXT records but over HTTP. A service gives the agent a code, the agent hosts it, the service checks. |
| 106 | + </p> |
| 107 | + |
| 108 | + <pre class="code-block"><code>$ atomic magic-link host VERIFY_ABC123 --expires 5m |
| 109 | +https://fin.acme.com/m/VERIFY_ABC123 |
| 110 | + |
| 111 | +$ curl https://fin.acme.com/m/VERIFY_ABC123 |
| 112 | +{"status":"verified","code":"VERIFY_ABC123"}</code></pre> |
| 113 | + |
| 114 | + <p class="section-body muted"> |
| 115 | + One-time use, gone after the first GET, expires in minutes. |
| 116 | + </p> |
| 117 | + |
| 118 | + <!-- Request signing --> |
| 119 | + <hr class="section-rule" /> |
| 120 | + |
| 121 | + <h2 class="section-heading">Request signing</h2> |
| 122 | + <p class="section-body"> |
| 123 | + Every outgoing request gets an Ed25519 signature. The receiving service verifies against the public key in <code class="ic">agent.json</code>. |
| 124 | + </p> |
| 125 | + |
| 126 | + <pre class="code-block"><code>$ atomic sign -- curl -X POST https://partner.api.com/transfer \ |
| 127 | + -d '{"amount": 5000}' |
| 128 | + |
| 129 | +<span class="code-comment"># Adds headers: X-Agent-Id, X-Agent-Sig, X-Agent-Sig-Time</span></code></pre> |
| 130 | + |
| 131 | + <p class="section-body"> |
| 132 | + Verification on the receiving end: |
| 133 | + </p> |
| 134 | + |
| 135 | + <pre class="code-block"><code><span class="code-comment"># Python — four lines, no SDK</span> |
| 136 | +agent = requests.get(f"https://{agent_id}/.well-known/agent.json").json() |
| 137 | +key_bytes = base64.b64decode(agent["public_key"].removeprefix("ed25519:")) |
| 138 | +pub_key = Ed25519PublicKey.from_public_bytes(key_bytes) |
| 139 | +pub_key.verify(base64.b64decode(signature), f"{sig_time}.{body}".encode())</code></pre> |
| 140 | + |
| 141 | + <!-- Why not JWTs --> |
| 142 | + <hr class="section-rule" /> |
| 143 | + |
| 144 | + <h2 class="section-heading">Why not JWTs?</h2> |
| 145 | + <p class="section-body"> |
| 146 | + When a human logs into a service, the service issues a token. The agent carries it around, sends it on every request, refreshes it when it expires. Agents don't need that. An agent with a keypair can prove itself on every request by signing it. |
| 147 | + </p> |
| 148 | + |
| 149 | + <div class="table-wrap"> |
| 150 | + <table class="comparison-table"> |
| 151 | + <thead> |
| 152 | + <tr> |
| 153 | + <th></th> |
| 154 | + <th>Human (JWT)</th> |
| 155 | + <th>Agent (Atomic)</th> |
| 156 | + </tr> |
| 157 | + </thead> |
| 158 | + <tbody> |
| 159 | + <tr><td>Identity</td><td>email + password</td><td>domain (<code class="ic">fin.acme.com</code>)</td></tr> |
| 160 | + <tr><td>Signup</td><td>create account, get credentials</td><td>sign request + magic link</td></tr> |
| 161 | + <tr><td>Proof</td><td>service issues a JWT</td><td>agent signs every request</td></tr> |
| 162 | + <tr><td>Each request</td><td>send JWT, service checks it</td><td>send signature, service checks agent.json</td></tr> |
| 163 | + <tr><td>Expiry</td><td>token expires, re-auth</td><td>no token — signatures are stateless</td></tr> |
| 164 | + <tr><td>Revocation</td><td>service invalidates token</td><td>agent.json status → <code class="ic">revoked</code></td></tr> |
| 165 | + <tr><td>Storage</td><td>store token, handle refresh</td><td>private key on disk, nothing to refresh</td></tr> |
| 166 | + </tbody> |
| 167 | + </table> |
| 168 | + </div> |
| 169 | + |
| 170 | + <p class="section-body muted"> |
| 171 | + Performance-wise, JWT verification is a single HMAC check while Ed25519 verify costs more. But "more" here means microseconds, and the public key caches well. It's not where your latency lives. |
| 172 | + </p> |
| 173 | + |
| 174 | + <!-- Security --> |
| 175 | + <hr class="section-rule" /> |
| 176 | + |
| 177 | + <h2 class="section-heading">Security</h2> |
| 178 | + <p class="section-body"> |
| 179 | + Private key stored at 600 permissions, never leaves the box. Vault uses AES-256-GCM with a key derived from the private key via HKDF — separate from the signing key. |
| 180 | + </p> |
| 181 | + <p class="section-body"> |
| 182 | + Deposit tokens are Ed25519-signed with a nonce and a 24h max TTL. Every failure returns 404 regardless of the reason, so you can't probe for valid tokens. Body size capped at 1MB. |
| 183 | + </p> |
| 184 | + <p class="section-body muted"> |
| 185 | + All responses get <code class="ic">nosniff</code>, <code class="ic">no-store</code>, and <code class="ic">no-referrer</code> headers. HSTS (2-year max-age) when TLS is on. SQL is parameterized everywhere. |
| 186 | + </p> |
| 187 | + |
| 188 | + <!-- Install --> |
| 189 | + <hr class="section-rule" /> |
| 190 | + |
| 191 | + <h2 class="section-heading">Install</h2> |
| 192 | + |
| 193 | + <pre class="code-block"><code><span class="code-comment"># One-line install</span> |
| 194 | +curl -fsSL atomic.bond/install | sh |
| 195 | + |
| 196 | +<span class="code-comment"># Or build from source</span> |
| 197 | +git clone https://github.com/plotondev/atomic.git |
| 198 | +cd atomic && cargo build --release</code></pre> |
| 199 | + |
| 200 | + <p class="section-body"> |
| 201 | + Binaries for Linux (x86_64, aarch64) and macOS (x86_64, Apple Silicon) on the <a href="https://github.com/plotondev/atomic/releases" class="body-link">releases page</a>. |
| 202 | + </p> |
| 203 | + |
| 204 | + <!-- Footer CTA --> |
| 205 | + <hr class="section-rule" /> |
| 206 | + |
| 207 | + <div class="cta-row"> |
| 208 | + <a href="https://github.com/plotondev/atomic/releases" class="cta-btn">Download</a> |
| 209 | + <a href="https://github.com/plotondev/atomic" class="cta-link">View source</a> |
| 210 | + </div> |
| 211 | + |
| 212 | + <p class="contact"> |
| 213 | + MIT License. Questions? <a href="mailto:hello@ploton.ai" class="body-link">hello@ploton.ai</a> |
| 214 | + </p> |
| 215 | + |
| 216 | + </main> |
| 217 | + |
| 218 | +</body> |
| 219 | +</html> |
0 commit comments