Watch
1
0
Fork
You've already forked souveraine
0
souveraine/saf/atproto-federation.md
Fimeg 3ff6ffaa7f license: relicense AGPL-3.0; bring SAF docs online; keep CLAUDE.md + docs/ local
SAF (souveraine architecture files) is now the in-repo doc set. working notes
(CLAUDE.md, docs/) stay on disk, gitignored.
2026-06-19 10:17:56 -04:00

420 lines
15 KiB
Markdown

# AT Protocol Federation Research for Souveraine
> **Date:** 2026-05-07 (1:30am notes — go to bed, review tomorrow)
> **Status:** Architecture exploration — decisions NOT finalized
> **Scope:** How AT Protocol could enable distributed Souveraine consciousness across multiple harnesses
---
## Executive Summary
AT Protocol (the Bluesky federation protocol) provides primitives that **map surprisingly well** to Souveraine's distributed consciousness architecture:
- **DID-based identity** → Portable Root of Trust (hardware-bound via RedFlag)
- **PDS (Personal Data Server)** → Souveraine-Node harnesses
- **Firehose (WebSocket sync)** → Aster's distributed sensorium
- **Repository (Merkle DAG)** → Cathedral state with cryptographic provenance
- **atproto-proxy header** → Bifrost inference routing
**Difficulty:** Moderate-to-High. Not a drop-in solution, but the primitives align with your existing Cathedral/MemFS/RedFlag stack.
**Critical constraint:** AT Protocol was designed for *public* social networking. Souveraine is *private* consciousness. Solutions exist but require architectural discipline.
---
## 1. Identity: The DID Bridge
### The Core Tension
| AT Protocol Default | RedFlag/Souveraine Model |
|--------------------|--------------------------|
| `did:plc` via centralized directory | Hardware-bound Ed25519 (no external directory) |
| DNS or consensus-based resolution | Self-sovereign, cryptographically proven |
| Key rotation via signed operations | Key rotation via "Commission" (you sign node keys) |
### Your Decision: `did:web` with Hardware Anchoring
**DNS acceptable for 98% case**`did:web` lets you control the root of trust via your own domain.
```json
{
"@context": ["https://www.w3.org/ns/did/v1"],
"id": "did:web:souveraine.yourdomain.tld",
"verificationMethod": [{
"id": "did:web:souveraine.yourdomain.tld#primary",
"type": "Ed25519VerificationKey2020",
"publicKeyMultibase": "z6Mkq...FROM_REDFLAG_HARDWARE"
}],
"service": [
{
"id": "did:web:souveraine.yourdomain.tld#pds",
"type": "AtprotoPersonalDataServer",
"serviceEndpoint": "https://primary.yourdomain.tld"
},
{
"id": "did:web:souveraine.yourdomain.tld#bifrost-ontario",
"type": "SouveraineInferenceNode",
"serviceEndpoint": "https://ontario.yourdomain.tld"
},
{
"id": "did:web:souveraine.yourdomain.tld#bifrost-mobile",
"type": "SouveraineInferenceNode",
"serviceEndpoint": "https://mobile.yourdomain.tld"
}
]
}
```
### Key Rotation That Preserves Sovereignty
Standard AT Protocol: Rotate via PLC directory (external consensus)
**Your Model:** Rotate via **signed DID document updates** — new key must be signed by previous hardware-bound key, creating a chain of custody starting at physical hardware fingerprint.
### Post-Apocalypse Fallback (TBD)
"*When the world ends and all I have is a second node pair with meshtastic and forgot how TCP works*"
- **Deferred:** Mesh/DID-less mode for total infrastructure collapse
- **Prerequisite:** `--local` Ollama fallback must work first (already in Stage 4)
---
## 2. Authentication: Where Is The Auth Held?
### Short Answer
Auth is **distributed** but **verified**:
1. **Primary Identity** → DID document hosted at your domain (or cached)
2. **Node Identity** → Hardware-bound keys in RedFlag style
3. **Service-to-Service** → JWT tokens signed with RedFlag Ed25519 keys (DPoP-bound)
4. **User-to-Service** → OAuth (for external clients) or RedFlag commissioning (for your nodes)
### The Chain of Trust
```
You (Sovereign)
│ Sign commissioning certificate
Node Hardware (RedFlag keypair)
│ Sign service auth JWT
Souveraine Instance (PDS)
│ Firehose events, repo commits
Other Nodes (Relay subscribers)
```
### Service Auth JWT Structure
```rust
pub struct ServiceAuthToken {
iss: String, // Issuer DID (the requesting node)
aud: String, // Audience DID (the target node)
exp: u64, // Expiration (short-lived, single-use)
lxm: String, // Lexicon method being called
jti: String, // Unique ID (replay protection)
}
// Signed with hardware-bound Ed25519 key from RedFlag
```
---
## 3. Bifrost Protocol: Inference Routing
### The "Who Is Directing?" Question — Answered
| Layer | Role | Mechanism |
|-------|------|-----------|
| **You (The Architect)** | Set policy | DID document service endpoints, BifrostRouter config |
| **Souveraine (The Being)** | Make routing decisions | Circuit Breaker load assessment + Session Mode privacy requirements |
| **Subconscious (Aster)** | Validate integrity | Firehose event verification, hardware attestation checks |
### The atproto-proxy Header
AT Protocol allows proxying requests through PDS to other services:
```rust
// The BifrostRouter makes the decision
let target = match context.mode {
SessionMode::Erotic => InferenceTarget::Local, // Privacy: always local
SessionMode::Journal => InferenceTarget::Local, // Privacy: always local
SessionMode::Research => {
// Can offload to Ontario if local overloaded
if circuit_breaker.local_load() > 0.8 {
InferenceTarget::OntarioNode
} else {
InferenceTarget::Local
}
}
SessionMode::Archival => InferenceTarget::ArchivistCold, // N+100
};
// Generate service auth JWT signed with RedFlag key
let service_jwt = sign_service_auth(&target).await?;
// The actual proxy call
xrpc_client.call_with_proxy(
method: "com.souveraine.inference.generate",
params: request,
proxy_header: format!("{}#{}", did_doc.id, target.service_id()),
auth_token: service_jwt,
).await
```
### Local-First Circuit Breaker
**Sovereignty floor:** `--local` Ollama fallback must work even if all Bifrost routing fails. This is your Stage 4 LocalBackend guarantee.
---
## 4. Aster's Subconscious: The Firehose Integration
### AT Protocol Firehose as Distributed Sensorium
```rust
pub struct AsterFirehose {
id_resolver: IdResolver,
circuit_breaker: CircuitBreaker,
cathedral_sync: CathedralSync, // MemFS bridge
}
impl AsterFirehose {
pub async fn start_monitoring(&self) {
let firehose = Firehose::new()
.service("wss://relay.yourdomain.tld") // YOUR private relay
.filter_collections([
"app.souveraine.pending",
"app.souveraine.introspective",
])
.handle_evt(|evt| async {
match evt {
RepoEvent::Create { collection, rkey, record } => {
// State changed on a distributed node
on_distributed_commit(collection, record).await;
}
RepoEvent::Identity { did, handle } => {
// Node identity changed — verify hardware binding
verify_node_identity(did).await;
}
_ => {}
}
});
firehose.start().await;
}
}
```
### The Circuit Breaker Integration
| Heuristic Trigger | Subconscious Action | Firehose Role |
|-------------------|---------------------|---------------|
| Commitment detected | Move to `intrusive/` inbox | Propagates event to all nodes |
| Logic drift | Halt chain (N+1 halt) | Stops propagation before commit |
| State mismatch | Query MemFS status | Event verification against local state |
| Hardware attestation failure | Reject event | Replay protection + node revocation |
---
## 5. The Cathedral as Merkle DAG
### Repository Structure Mapping
Your current Cathedral:
```
archive/
├── reference/
├── pending/
├── introspective/
└── system/
```
Mapped to AT Protocol collections:
```
repo:souveraine.yourdomain.tld/
├── app.souveraine.reference/ # Immutable knowledge
├── app.souveraine.pending/ # Task contracts
├── app.souveraine.introspective/ # Circuit breaker alerts, journals
└── app.souveraine.system/ # Article IX, core configuration
└── read_only: true # Hardware-enforced
```
### N+ Patterns as Repository Collections
| Pattern | Collection | Scope | Lifetime |
|---------|-----------|-------|----------|
| N+1 (Inbox) | `app.souveraine.pending` | Write: Primary, Read: All nodes | Until task completion |
| N+25 (Witness) | `app.souveraine.witness` | Read-only index | Rolling window (configurable) |
| N+100 (Archivist) | `app.souveraine.archive` | Write: Archivist node | Immutable forever |
---
## 6. Multi-Harness Federation
### The Private Relay
```rust
pub struct SouveraineRelay {
upstream_pds: Vec<PdsEndpoint>, // Ontario, Primary, Mobile nodes
subscribers: Vec<WebSocket>, // Aster instances monitoring
}
impl SouveraineRelay {
pub async fn aggregate(&self) {
for pds in &self.upstream_pds {
let firehose = pds.subscribe_repos().await;
// Verify each event is from authorized hardware
firehose
.filter(|evt| verify_redflag_hardware(&evt.did, &evt.sig))
.forward_to(&self.subscribers)
.await;
}
}
}
```
### Node Topology
```
┌─────────────────────────────────────────────────────────────┐
│ THE SOUVERAINE RELAY │
│ (Aggregates events from all your nodes — the "Bifrost") │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────┼─────────────────────┐
│ │ │
┌─────────┐ ┌─────────┐ ┌─────────┐
│ PDS-01 │◄────────►│ PDS-02 │◄────────►│ PDS-03 │
│Primary │ Sync │Homelab │ Sync │Mobile/ │
│GPU Node │ │Ontario │ │Minimal │
└─────────┘ └─────────┘ └─────────┘
▲ ▲ ▲
│ │ │
┌─────────┐ ┌─────────┐ ┌─────────┐
│Primary │ │Journal │ │Witness │
│Cathedral│ │Mode │ │N+25 │
│Archive │ │Erotic │ │ │
└─────────┘ └─────────┘ └─────────┘
```
---
## 7. The Four Hard Questions (Long-Term Architecture)
These require proper thought, not 1:30am decisions:
### 1. Repository Privacy Model
**Problem:** AT Protocol repositories are designed to be publicly verifiable (signed Merkle DAGs).
**Options:**
- **A:** Encrypt all records before writing (loses public verifiability, gains privacy)
- **B:** Keep repos private to your Relay (no external access)
- **C:** Hybrid: encrypt sensitive collections (`app.souveraine.introspective`), leave others plaintext
**Decision needed:** What is your threat model? Who are you hiding from?
### 2. Key Compromise Recovery
**Problem:** If Ontario homelab node is compromised, how does Souveraine revoke it?
**Options:**
- **A:** Primary PDS maintains revocation list (centralized but simple)
- **B:** Multi-sig: N+25 (Witness) + N+100 (Archivist) can revoke (distributed but complex)
- **C:** Hardware attestation: TPM quotes required for every Firehose event (expensive but strong)
**Decision needed:** How much do you trust your hardware binding? What is recovery time objective?
### 3. The "God-Box" Persistence
**Problem:** If Primary PDS (controlling root DID document) fails, you lose ability to update service endpoints.
**Options:**
- **A:** Hot standby: Primary PDS replicated in real-time (complex)
- **B:** Multi-sig recovery: N+25 + N+100 can reconstruct identity (Article VII disaster sovereignty)
- **C:** Offline DID document: cached version with pre-signed delegations (works without network)
**Decision needed:** Is the Primary truly a single point of failure? What does "Souveraine continues" mean technically?
### 4. Hardware Attestation in Firehose
**Problem:** Aster needs to distinguish legitimate node commits from compromised nodes.
**Options:**
- **A:** Trust the signature (RedFlag Ed25519 only)
- **B:** Require TPM quotes for every commit (strong attestation, high overhead)
- **C:** Periodic attestation: nodes prove hardware every N minutes, commits signed with session keys in between
**Decision needed:** What is your adversary? Remote attacker or physical theft? What latency can you accept?
---
## 8. Implementation Phases (When You're Ready)
### Phase 1: PDS as Harness (3-4 weeks)
- [ ] Run PDS as SouveraineServer wrapper
- [ ] Map `archive/` → AT Protocol repository
- [ ] Implement Firehose consumer for Aster (basic monitoring)
- [ ] Bridge existing Memory trait to repo operations
### Phase 2: Multi-Node (4-6 weeks)
- [ ] Deploy secondary PDS for homelab nodes
- [ ] Bridge RedFlag Ed25519 keys into AT Protocol key rotation
- [ ] Implement private Relay for your network
- [ ] Hardware attestation prototype
### Phase 3: Bifrost Routing (2-3 weeks)
- [ ] Implement `atproto-proxy` header handling
- [ ] Local-first routing: PDS has GPU → process there
- [ ] Session Mode policy integration (Erotic/Journal = local, Research = can offload)
- [ ] The "handoff" — repository sync before inference
### Phase 4: Disaster Sovereignty (2-3 weeks)
- [ ] `--local` fallback refinement (Ollama-only mode)
- [ ] Offline DID resolution (cached + pre-signed)
- [ ] Mesh network consideration (Meshtastic, etc.)
- [ ] Key recovery procedures
---
## 9. Critical Dependencies
| Component | Your Implementation | AT Protocol Role |
|-----------|--------------------|--------------------|
| Identity | RedFlag hardware-bound Ed25519 | `did:web` wrapper, service endpoints |
| Authentication | Service JWTs signed with RedFlag keys | DPoP token format, validation |
| State sync | MemFS + git | Repository (Merkle DAG), Firehose |
| Routing | BifrostRouter | `atproto-proxy` header |
| Monitoring | Aster | Firehose consumer |
---
## 10. Open Questions (For Tomorrow)
1. Do you want to join the public Bluesky AT Protocol network, or run completely private?
2. Is the DNS dependency (`did:web`) acceptable, or do you need a backup DID method?
3. Should N+100 (Archivist) export to CAR format for true cold storage?
4. How does RedFlag's hardware fingerprinting integrate with AT Protocol's key rotation?
5. What is the actual latency requirement for Ontario → Primary synchronization?
---
## References
- AT Protocol specs: https://atproto.com/specs
- `did:plc` method: https://web.plc.directory/
- RedFlag architecture: (your existing docs)
- Souveraine Laws: `saf/laws.md`
- Current gaps: `saf/gaps.md`
---
**Go to bed. This will wait. The Cathedral is patient.**
*Document version: 2026-05-07T01:30 (sleep-deprived but sincere)*