diff --git a/.gitignore b/.gitignore index 2768254..1b0372b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ .env .claude/ CLAUDE.md +.lambda-rlm-cache/ +src/.*.md +src/.*.log diff --git a/Cargo.lock b/Cargo.lock index 6e04180..e420118 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ dependencies = [ "ctr", "ghash", "subtle", + "zeroize", ] [[package]] @@ -46,15 +47,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstream" version = "0.6.21" @@ -128,13 +120,11 @@ dependencies = [ "anyhow", "axum", "axum-server", - "base64", + "base64ct", "bytes", - "chrono", "clap", - "dirs", "ed25519-dalek", - "hex", + "fs2", "hkdf", "rand 0.8.5", "reqwest", @@ -143,6 +133,8 @@ dependencies = [ "serde_json", "sha2", "thiserror", + "tikv-jemalloc-ctl", + "tikv-jemallocator", "tokio", "tower-http", "tracing", @@ -321,20 +313,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - [[package]] name = "cipher" version = "0.4.4" @@ -406,12 +384,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -489,27 +461,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", -] - [[package]] name = "displaydoc" version = "0.2.5" @@ -629,6 +580,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -770,12 +731,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "hkdf" version = "0.12.4" @@ -902,30 +857,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "2.1.1" @@ -1107,15 +1038,6 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" -[[package]] -name = "libredox" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" -dependencies = [ - "libc", -] - [[package]] name = "libsqlite3-sys" version = "0.32.0" @@ -1192,15 +1114,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -1220,10 +1133,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] -name = "option-ext" -version = "0.2.0" +name = "paste" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" @@ -1427,17 +1340,6 @@ dependencies = [ "getrandom 0.3.4", ] -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror", -] - [[package]] name = "regex-automata" version = "0.4.14" @@ -1812,6 +1714,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -2184,39 +2117,26 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.62.2" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "windows-implement" -version = "0.60.2" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "windows-interface" -version = "0.59.3" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-link" @@ -2224,24 +2144,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index a2b1c38..f9a01af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,10 @@ repository = "https://github.com/ploton/atomic" name = "atomic" path = "src/main.rs" +[features] +default = ["jemalloc"] +jemalloc = ["dep:tikv-jemallocator", "dep:tikv-jemalloc-ctl"] + [dependencies] # CLI clap = { version = "4", features = ["derive"] } @@ -30,7 +34,7 @@ serde_json = "1" ed25519-dalek = { version = "2", features = ["rand_core"] } # Crypto - AES-256-GCM -aes-gcm = "0.10" +aes-gcm = { version = "0.10", features = ["zeroize"] } # Key derivation hkdf = "0.12" @@ -38,15 +42,11 @@ sha2 = "0.10" # Random + encoding rand = "0.8" -base64 = "0.22" -hex = "0.4" +base64ct = { version = "1", features = ["alloc"] } # HTTP client (for verify command) reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false } -# Time -chrono = { version = "0.4", features = ["serde"] } - # Logging tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } @@ -60,8 +60,10 @@ rusqlite = { version = "0.34", features = ["bundled"] } # Misc bytes = "1" -dirs = "6" zeroize = { version = "1.8.2", features = ["derive"] } +fs2 = "0.4" +tikv-jemallocator = { version = "0.6", optional = true } +tikv-jemalloc-ctl = { version = "0.6", optional = true } [profile.release] opt-level = 3 diff --git a/README.md b/README.md index eb451d3..1166556 100644 --- a/README.md +++ b/README.md @@ -78,20 +78,6 @@ $ atomic deposits $ atomic deposits --label stripe_key ``` -## Magic links - -Domain verification, like DNS TXT records but over HTTP. A service gives the agent a code, the agent hosts it, the service checks. - -```bash -$ atomic magic-link host VERIFY_ABC123 --expires 5m -https://fin.acme.com/m/VERIFY_ABC123 - -$ curl https://fin.acme.com/m/VERIFY_ABC123 -{"status":"verified","code":"VERIFY_ABC123"} -``` - -One-time use, gone after the first GET, expires in minutes. - ## Request signing ```bash @@ -118,7 +104,7 @@ Agents don't need that. An agent with a keypair can prove itself on every reques | | Human (JWT) | Agent (Atomic) | |------------------|--------------------------------------|---------------------------------------------| | **Identity** | email + password | domain (`fin.acme.com`) | -| **Signup** | create account, get credentials | sign request + magic link for domain proof | +| **Signup** | create account, get credentials | sign request, service verifies agent.json | | **Proof** | service issues a JWT | agent signs every request with private key | | **Each request** | send JWT, service checks it | send signature, service checks agent.json | | **Expiry** | token expires, agent re-auths | no token -- signatures are stateless | @@ -128,7 +114,7 @@ Agents don't need that. An agent with a keypair can prove itself on every reques The practical difference: the agent has nothing to manage. No token storage, no refresh logic. The private key stays on the box and never gets sent over the wire. -A service that wants extra assurance can layer a magic link challenge on top of the signature check at signup -- verify the sig, then confirm domain control, then create an internal account for `fin.acme.com`. After that, subsequent requests are just signature checks against a cached public key. +A service that wants extra assurance can verify domain control via DNS TXT records on top of the signature check at signup. After that, subsequent requests are just signature checks against a cached public key. Performance-wise, JWT verification is a single HMAC check while Ed25519 verify costs more. But "more" here means microseconds, and the public key only changes on rotation so it caches well. It's not where your latency lives. @@ -146,9 +132,6 @@ atomic verify Check another agent atomic deposit-url --label --expires Create deposit URL atomic deposits [--label ] Deposit audit log -atomic magic-link host --expires Host a verification code -atomic magic-link list Show active codes - atomic vault set