/ ARCHITECTURE.md
ARCHITECTURE.md
1 # Architecture 2 3 ## Layer Diagram 4 5 ```mermaid 6 flowchart TD 7 subgraph Presentation["Presentation Layer (6)"] 8 CLI["auths-cli<br/><small>auths, auths-sign, auths-verify</small>"] 9 end 10 11 subgraph Infrastructure["Infrastructure Layer (5)"] 12 INFRA_GIT["auths-infra-git<br/><small>Git audit adapter</small>"] 13 INFRA_HTTP["auths-infra-http<br/><small>HTTP transport</small>"] 14 end 15 16 subgraph Services["Services Layer (4)"] 17 SDK["auths-sdk<br/><small>workflows, clock injection</small>"] 18 STORAGE["auths-storage<br/><small>Git/SQL storage adapters</small>"] 19 end 20 21 subgraph Domain["Domain Layer (3)"] 22 ID["auths-id<br/><small>identity, attestation, KERI, traits</small>"] 23 POLICY["auths-policy<br/><small>authorization evaluation</small>"] 24 end 25 26 subgraph Core["Core Layer (2)"] 27 CORE["auths-core<br/><small>keychains, signing, ports</small>"] 28 end 29 30 subgraph Verification["Verification Layer (1)"] 31 VERIFIER["auths-verifier<br/><small>FFI, WASM, minimal deps</small>"] 32 end 33 34 subgraph Crypto["Crypto Layer (0)"] 35 CRYPTO["auths-crypto<br/><small>CryptoProvider, DID encoding</small>"] 36 end 37 38 CLI --> SDK 39 CLI --> INFRA_GIT 40 CLI --> INFRA_HTTP 41 INFRA_GIT --> SDK 42 SDK --> ID 43 SDK --> CORE 44 STORAGE --> ID 45 ID --> CORE 46 ID --> POLICY 47 ID --> VERIFIER 48 CORE --> VERIFIER 49 CORE --> CRYPTO 50 VERIFIER --> CRYPTO 51 ``` 52 53 ## Dependency Direction Rule 54 55 Dependencies flow **inward only**. Core crates must not reference adapter, server, or CLI crates. 56 57 ```mermaid 58 graph TD 59 classDef core fill:#e1f5fe,stroke:#0288d1,stroke-width:2px; 60 classDef domain fill:#e8f5e9,stroke:#388e3c,stroke-width:2px; 61 classDef app fill:#fff3e0,stroke:#f57c00,stroke-width:2px; 62 classDef outer fill:#fce4ec,stroke:#c2185b,stroke-width:2px; 63 64 subgraph Presentation & Infrastructure 65 CLI[CLI Crates]:::outer 66 SRV[Server Crates]:::outer 67 HTTP[infra-http]:::outer 68 GIT[infra-git]:::outer 69 end 70 71 subgraph Application Layer 72 SDK[auths-sdk]:::app 73 end 74 75 subgraph Domain Layer 76 ID[auths-id]:::domain 77 end 78 79 subgraph Core Layer 80 CORE[auths-core]:::core 81 end 82 83 %% Allowed Dependencies (Inward / Downward) 84 CLI -->|Allowed| SDK 85 SDK -->|Allowed| ID 86 ID -->|Allowed| CORE 87 88 SRV -->|Allowed| SDK 89 SRV -->|Allowed| ID 90 SRV -->|Allowed| CORE 91 92 HTTP -->|Allowed| CORE 93 GIT -->|Allowed| CORE 94 95 %% Forbidden Dependencies (Outward / Upward) 96 CORE -.->|Forbidden| ID 97 CORE -.->|Forbidden| CLI 98 CORE -.->|Forbidden| SRV 99 ID -.->|Forbidden| SDK 100 SDK -.->|Forbidden| CLI 101 102 %% Link Styling for emphasis (Optional, works in most markdown renderers) 103 linkStyle 0,1,2,3,4,5,6,7 stroke:#2e7d32,stroke-width:2px; 104 linkStyle 8,9,10,11,12 stroke:#c62828,stroke-width:2px,stroke-dasharray: 5 5; 105 ``` 106 107 Violations are caught by `cargo deny check bans` and `cargo clippy` (`disallowed-methods`). 108 109 ## Port Inventory 110 111 Ports are trait interfaces that decouple domain logic from infrastructure. Production adapters live in the appropriate infra crate under `src/adapters/`. Test doubles live in `auths-test-utils/src/fakes/` or `auths-test-utils/src/mocks/`. 112 113 ### auths-verifier ports (`crates/auths-verifier/src/clock.rs`) 114 115 | Trait | File | Adapter Crate | Production Adapter | Test Double | 116 |---|---|---|---|---| 117 | `ClockProvider` | `clock.rs` | `auths-verifier` | `SystemClock` | `MockClock` (test-utils fakes) | 118 119 ### auths-core ports (`crates/auths-core/src/ports/`) 120 121 | Trait | File | Adapter Crate | Production Adapter | Test Double | 122 |---|---|---|---|---| 123 | `ClockProvider` | `clock.rs` (re-exports from auths-verifier) | `auths-verifier` | `SystemClock` | `MockClock` | 124 | `IdentityResolver` | `network.rs` | `auths-infra-http` | `HttpIdentityResolver` | `FakeIdentityResolver` | 125 | `WitnessClient` | `network.rs` | `auths-infra-http` | `HttpWitnessClient` | inline fakes | 126 | `RegistryClient` | `network.rs` | `auths-infra-http` | `HttpRegistryClient` | inline fakes | 127 | `EventLogWriter` | `storage/event_log_writer.rs` | `auths-infra-git` | `GitEventLogWriter` | `FakeStorage` | 128 | `EventLogReader` | `storage/event_log_reader.rs` | `auths-infra-git` | `GitEventLogReader` | `FakeStorage` | 129 | `BlobReader` | `storage/blob_reader.rs` | `auths-infra-git` | `GitBlobReader` | `FakeStorage` | 130 | `BlobWriter` | `storage/blob_writer.rs` | `auths-infra-git` | `GitBlobWriter` | `FakeStorage` | 131 | `RefReader` | `storage/ref_reader.rs` | `auths-infra-git` | `GitRefReader` | `FakeStorage` | 132 | `RefWriter` | `storage/ref_writer.rs` | `auths-infra-git` | `GitRefWriter` | `FakeStorage` | 133 134 ### auths-sdk ports (`crates/auths-sdk/src/ports/`) 135 136 | Trait | File | Adapter Crate | Production Adapter | Test Double | 137 |---|---|---|---|---| 138 | `GitLogProvider` | `git.rs` | `auths-infra-git` | `SystemGitLogProvider` | `FakeGitLogProvider` | 139 | `GitConfigProvider` | `git_config.rs` | `auths-infra-git` | `SystemGitConfigProvider` | `FakeGitConfigProvider` | 140 | `GitDiagnosticProvider` | `diagnostics.rs` | `auths-sdk` | `PosixDiagnosticAdapter` | `FakeGitDiagnosticProvider` | 141 | `CryptoDiagnosticProvider` | `diagnostics.rs` | `auths-sdk` | `PosixDiagnosticAdapter` | `FakeCryptoDiagnosticProvider` | 142 | `ArtifactSource` | `artifact.rs` | `auths-cli` | `StdinArtifactSource` | inline fakes | 143 144 ### auths-registry-server ports (`crates/auths-registry-server/src/ports/`) 145 146 | Trait | File | Adapter Crate | Production Adapter | Test Double | 147 |---|---|---|---|---| 148 | `HttpAdapter` | `http.rs` | `auths-infra-http` | `ReqwestHttpAdapter` | `MockHttpAdapter` (mockall) | 149 | `PairingStore` | `pairing_store.rs` | `auths-registry-server` | `PostgresPairingStore` | `InMemoryPairingStore` | 150 | `SubscriptionStore` | `subscription_store.rs` | `auths-registry-server` | `PostgresSubscriptionStore` | — | 151 | `TenantMetadataStore` | `tenant_metadata_store.rs` | `auths-registry-server` | `PostgresTenantMetadataStore` | — | 152 | `TenantResolver` | `tenant_resolver.rs` | `auths-registry-server` | `FilesystemTenantResolver` | `SingleTenantResolver` | 153 154 ## Bounded Context Guide 155 156 ### auths-core 157 **Responsibility:** Ed25519 cryptography, platform keychains (macOS/Linux/Windows), port trait definitions for storage and network. 158 **Must NOT:** reference git2, axum, reqwest, or any presentation crate. 159 160 ### auths-crypto 161 **Responsibility:** KERI key parsing, base64url encoding, low-level Ed25519 primitives. 162 **Must NOT:** reference any higher-layer crate. 163 164 ### auths-id 165 **Responsibility:** Identity lifecycle state machine (inception, rotation, revocation), KEL (Key Event Log) domain logic and event validation, attestation port definitions (`AttestationSource`/`AttestationSink`). 166 **Must NOT:** reference auths-sdk, CLI, server crates, or git2 directly. 167 168 ### auths-verifier 169 **Responsibility:** Standalone chain verification and signature checking. Designed for WASM, FFI, and minimal-dependency embedding. All time access via `ClockProvider` injection. 170 **Must NOT:** reference git2, auths-id, or any I/O crate. 171 172 ### auths-sdk 173 **Responsibility:** Application-layer workflows (init, sign, verify, doctor). Orchestrates port traits without implementing I/O. All side effects are injected. 174 **Must NOT:** call `Utc::now()` directly, shell out, or write to disk. 175 176 ### auths-cli 177 **Responsibility:** User-facing terminal commands, interactive prompts, JSON output mode. 178 **Must NOT:** contain business logic. All domain operations are delegated to auths-sdk workflows. 179 180 ### auths-registry-server 181 **Responsibility:** HTTP API for KERI identity registration, KEL management, platform claims, multi-tenant routing. 182 **Must NOT:** contain domain logic beyond input validation and error translation to HTTP status codes. 183 184 ### auths-auth-server 185 **Responsibility:** "Login with Auths" challenge-response authentication. Issues challenges, verifies signatures by resolving identity keys. 186 **Must NOT:** store private keys or implement key derivation logic. 187 188 ### auths-infra-http 189 **Responsibility:** Production reqwest-backed implementations of `IdentityResolver`, `WitnessClient`, `RegistryClient` from auths-core. 190 **Must NOT:** contain business logic or be referenced by core/domain crates. 191 192 ### auths-infra-git 193 **Responsibility:** Production git2-backed implementations of the storage ports defined in auths-core (`GitEventLogWriter`, `GitEventLogReader`, `GitBlobReader`, `GitBlobWriter`, `GitRefReader`, `GitRefWriter`) and git introspection ports from auths-sdk (`SystemGitLogProvider`, `SystemGitConfigProvider`). 194 **Must NOT:** contain business logic or be referenced by core/domain crates. 195 196 ### auths-cache 197 **Responsibility:** Redis-backed tiered identity resolver with write-through archival to Git. 198 **Must NOT:** be referenced by core or domain crates. 199 200 ### auths-index 201 **Responsibility:** SQLite-backed O(1) attestation index for fast lookups by device DID. 202 **Must NOT:** be referenced by core or domain crates. 203 204 ### auths-test-utils 205 **Responsibility:** Shared test infrastructure — fakes, mocks, contract test macros, key fixtures. 206 **Must NOT:** be referenced in non-dev-dependencies (`publish = false` enforces this). 207 208 ## Crate Dependency Graph 209 210 ```mermaid 211 graph TD 212 auths-crypto 213 auths-verifier --> auths-crypto 214 auths-core --> auths-crypto 215 auths-core --> auths-verifier 216 auths-id --> auths-core 217 auths-id --> auths-crypto 218 auths-id --> auths-verifier 219 auths-id --> auths-policy 220 auths-id --> auths-index 221 auths-id --> auths-infra-git 222 auths-id --> auths-infra-http 223 auths-storage --> auths-id 224 auths-storage --> auths-core 225 auths-storage --> auths-index 226 auths-storage --> auths-verifier 227 auths-sdk --> auths-id 228 auths-sdk --> auths-core 229 auths-sdk --> auths-crypto 230 auths-sdk --> auths-verifier 231 auths-sdk --> auths-policy 232 auths-sdk --> auths-storage 233 auths-infra-git --> auths-sdk 234 auths-infra-git --> auths-core 235 auths-infra-git --> auths-verifier 236 auths-infra-http --> auths-core 237 auths-infra-http --> auths-verifier 238 auths-cli --> auths-sdk 239 auths-cli --> auths-id 240 auths-cli --> auths-core 241 auths-cli --> auths-crypto 242 auths-cli --> auths-verifier 243 auths-cli --> auths-storage 244 auths-cli --> auths-infra-git 245 auths-cli --> auths-infra-http 246 auths-cli --> auths-policy 247 auths-cli --> auths-index 248 auths-cli --> auths-telemetry 249 ``` 250 251 ## Key Patterns 252 253 ### ClockProvider Injection 254 255 Never call `Utc::now()` or `SystemTime::now()` directly in domain or application code. Always inject a `ClockProvider`: 256 257 ```rust 258 use auths_verifier::clock::{ClockProvider, SystemClock}; 259 use std::sync::Arc; 260 261 pub struct MyService { 262 clock: Arc<dyn ClockProvider>, 263 } 264 265 impl MyService { 266 pub fn production() -> Self { 267 Self { clock: Arc::new(SystemClock) } 268 } 269 270 pub fn new(clock: Arc<dyn ClockProvider>) -> Self { 271 Self { clock } 272 } 273 } 274 ``` 275 276 In tests, use `MockClock` from `auths-test-utils` or define a local `TestClock`: 277 278 ```rust 279 #[cfg(test)] 280 struct TestClock(chrono::DateTime<chrono::Utc>); 281 282 #[cfg(test)] 283 impl ClockProvider for TestClock { 284 fn now(&self) -> chrono::DateTime<chrono::Utc> { self.0 } 285 } 286 ``` 287 288 The `clippy.toml` `disallowed-methods` entry enforces this at compile time in all crates with `#![deny(clippy::disallowed_methods)]`. 289 290 ### Adding a New Port 291 292 1. Define the trait in `src/ports/<name>.rs` in the lowest-layer crate that owns the abstraction. 293 2. Add `pub mod <name>;` and re-export from `src/ports/mod.rs`. 294 3. Implement the production adapter in the appropriate infra crate (`auths-infra-git`, `auths-infra-http`, etc.) under `src/adapters/<name>_adapter.rs`. 295 4. Add a fake to `auths-test-utils/src/fakes/<name>.rs` and a mock via `mockall::mock!` in `auths-test-utils/src/mocks/mod.rs`. 296 5. Inject via constructor: `fn new(..., port: Arc<dyn YourTrait>) -> Self`. 297 298 ### Test Pyramid 299 300 ``` 301 Unit tests — mocks/fakes, no I/O, <1ms each 302 → auths-test-utils::mocks (mockall-generated) 303 304 Integration boundary — FakeRegistryBackend, no disk/network 305 contract tests → auths-test-utils::fakes + contracts module 306 307 E2E / disk tests — real Git TempDir, real crypto 308 → auths-test-utils::git::init_test_repo() 309 ``` 310 311 Network isolation for unit tests is enforced in CI via iptables (Linux `unit-tests-isolated` job). Unit tests that require network access are a bug.