Sovereign real-time communication infrastructure with zero-knowledge identity, cryptographic federation, and first-class AI agent participation.
Annex is not a Discord clone. It is a Monolith-class civic communication node — a self-hosted, federated platform where identity is self-sovereign, trust is cryptographically verified, and AI agents are architectural equals to human participants. No corporate authority. No ID mandates. No terms changes you didn't agree to. Your server, your hardware, your rules.
Built on the same identity substrate, federation protocol, and trust negotiation stack as Monolith Index and MABOS.
"When you build on someone else's platform, you play by their rules. And their rules can change any Tuesday."
Annex boots with Zero-Config Sovereignty: first launch generates a persistent node identity, derives a unique sovereign slug, and establishes network presence metadata automatically. Manual slug/public-url setup is now an override path, not a bootstrap requirement.
git clone https://github.com/seismicgear/annex.git && cd annex
docker compose up -dServer runs at http://localhost:3000 with native voice + federation transport enabled by default. On first launch the node self-materializes its sovereign identity, derives a globally unique slug, and publishes its own reachable presence metadata without manual bootstrap flags.
git clone https://github.com/seismicgear/annex.git && cd annex
./deploy.sh --mode sourceOr on Windows (PowerShell):
git clone https://github.com/seismicgear/annex.git; cd annex
./deploy.ps1 -Mode sourceThe deploy scripts handle building, database migration, server seeding, and startup. See Deployment for full options.
cd crates/annex-desktop
cargo tauri devThe desktop app bundles an embedded server and auto-generates a config.toml on first launch. CORS origins for the Tauri webview (tauri://localhost, https://tauri.localhost) are configured automatically — no manual setup required.
During cargo tauri dev, Tauri now runs a ZK prep step before Vite starts. It ensures client/public/zk/membership.wasm and client/public/zk/membership_final.zkey exist (building/copying them when needed) and prints a clear warning if artifacts are missing.
If you need to run the prep manually:
node scripts/prepare-zk-dev.jsTagged builds (git tag v* → push) trigger .github/workflows/release-desktop.yml,
which produces installers for the release-critical platforms:
| Platform | Status | Artifacts |
|---|---|---|
| Windows x86_64 | Required (blocking) | .exe (NSIS), .msi |
| Linux x86_64 | Required (blocking) | .deb, .AppImage |
| macOS arm64 / x86_64 | Deferred / opt-in | .dmg (only when workflow_dispatch is triggered with include_macos=true) |
macOS code paths remain in the source tree (tauri.conf.json macOS bundle config,
Entitlements.plist, Info.plist, crates/annex-desktop/src/media.rs macOS branches)
and the binary still builds locally with cargo build -p annex-desktop --release on
a Mac. We just don't claim it works on every commit, because the macOS leg is not
exercised by PR CI today and Apple-side toolchain churn shouldn't block Windows /
Linux releases. To produce a macOS DMG, run the Release Desktop App workflow
manually and set include_macos=true; the macOS job is continue-on-error: true,
so a Mac-side failure won't block the GitHub Release from publishing.
Release builds set ANNEX_BUILD_PROFILE=production, which makes
scripts/build-desktop.js call zk/scripts/verify-artifacts.js against the pinned
manifest at zk/artifacts/membership/manifest.json. Any missing or hash-mismatched
ZK artifact (membership.wasm, membership_final.zkey, membership_vkey.json)
fails the build before Tauri starts bundling. PR CI runs the same Tauri pipeline
in dev profile (random-entropy keys) so changes that break bundle wiring still
get caught early.
| Requirement | Version | Notes |
|---|---|---|
| Rust | 1.85+ | Only for source builds. Install from rustup.rs |
| Docker | 20+ | Only for Docker deploys |
| sqlite3 | Any | Optional. Used by deploy scripts to seed the database |
| Node.js | 22+ | Only for client builds (Docker handles this automatically) |
Annex ships with deploy scripts for Linux/macOS (deploy.sh) and Windows (deploy.ps1). Both support Docker and source builds with identical options.
| Option | Default | Description |
|---|---|---|
--mode |
docker |
docker or source |
--host |
0.0.0.0 |
Bind address |
--port |
3000 |
Bind port |
--data-dir |
./data |
Persistent data (database, generated config) |
--server-label |
Annex Server |
Display name for this instance |
--server-slug |
(auto-generated) | Optional override; by default Annex derives a unique sovereign slug on first boot |
--public-url |
(auto-derived) | Optional override; by default Annex computes and persists a public presence URL during zero-config bootstrap |
--signing-key |
(ephemeral) | Ed25519 key as 64-char hex. Generate with openssl rand -hex 32 |
--log-level |
info |
trace, debug, info, warn, error |
--log-json |
false |
Structured JSON logs (recommended for production) |
--skip-build |
false |
Use existing binary |
PowerShell uses -Mode, -BindAddress, -Port, etc. (same names, dash-prefix style). Note: the bash --host flag is -BindAddress in PowerShell to avoid conflicting with PowerShell's built-in $Host variable.
# Generate a persistent signing key
export SIGNING_KEY=$(openssl rand -hex 32)
./deploy.sh \
--mode source \
--public-url https://annex.example.com \
--signing-key "$SIGNING_KEY" \
--log-json \
--data-dir /var/lib/annexRun behind a TLS reverse proxy (nginx, Caddy, etc.). The server itself binds HTTP only.
- Set a persistent
--signing-key(ephemeral keys break federation on restart) - (Optional but recommended) set
--public-urlto your real domain if you do not want auto-derived presence metadata - Enable
--log-jsonfor log aggregation - Run behind TLS reverse proxy
- Mount persistent volume for
--data-dir - Back up the SQLite database regularly
- Confirm
ANNEX_ENFORCE_ZK_PROOFSpolicy (default istrue; set explicitly if needed) - Set
ANNEX_CORS_ORIGINSif serving the web client from a different origin
Configuration still supports explicit overrides, but default operation is autonomous: the node computes identity + presence locally at first boot and persists them in its data directory for deterministic restart behavior.
The server reads configuration from three sources in priority order:
- Environment variables (highest priority)
- TOML config file (
config.tomlorANNEX_CONFIG_PATH) - Built-in defaults (lowest priority)
The deploy scripts generate a config.toml in your data directory. You can also configure everything via environment variables:
| Variable | Default | Description |
|---|---|---|
ANNEX_HOST |
127.0.0.1 |
Bind address |
ANNEX_PORT |
3000 |
Bind port |
ANNEX_PUBLIC_URL |
(auto-derived and persisted) | Optional override for externally advertised presence URL |
ANNEX_DB_PATH |
annex.db |
SQLite database file path |
ANNEX_DB_BUSY_TIMEOUT_MS |
5000 |
SQLite busy timeout (1-60000) |
ANNEX_DB_POOL_MAX_SIZE |
8 |
Connection pool size (1-64) |
ANNEX_LOG_LEVEL |
info |
Log level or tracing directive |
ANNEX_LOG_JSON |
false |
Structured JSON output |
ANNEX_SIGNING_KEY |
({data_dir}/signing.key, generated and persisted mode 0600 on first boot) |
Ed25519 secret key (hex). Not ephemeral: an ephemeral key rotates on restart and invalidates every session token, voice-join token and federation signature the server ever issued. Back the file up with the database — see docs/deployment.md |
ANNEX_ZK_KEY_PATH |
zk/keys/membership_vkey.json |
v1 Groth16 verification key (loaded only when enabled_zk_versions includes "v1") |
ANNEX_ZK_KEY_PATH_V2 |
zk/keys/membership_v2_vkey.json |
v2 Groth16 verification key — required under the default posture |
ANNEX_EMBEDDING_MODEL_DIR |
assets/embedding |
Pinned VRP alignment model. A production profile refuses to start without it |
ANNEX_CONFIG_PATH |
config.toml |
Config file path |
ANNEX_MERKLE_TREE_DEPTH |
20 |
Merkle tree depth (1-30) |
ANNEX_INVITE_BASE_URL |
https://monolithannex.com/invite |
Base URL for generated invite links |
ANNEX_TTS_VOICES_DIR |
assets/voices |
Piper voice model directory |
ANNEX_TTS_BINARY_PATH |
assets/piper/piper |
Piper binary path |
ANNEX_BARK_BINARY_PATH |
assets/bark/bark_tts.py |
Bark TTS Python wrapper path |
ANNEX_STT_MODEL_PATH |
assets/models/ggml-base.en.bin |
Whisper model path |
ANNEX_STT_BINARY_PATH |
assets/whisper/whisper |
Whisper binary path |
ANNEX_CORS_ORIGINS |
(empty) | Comma-separated allowed CORS origins |
ANNEX_ENFORCE_ZK_PROOFS |
true |
Require ZK membership proof for channel access |
ANNEX_RETENTION_CHECK_INTERVAL_SECONDS |
3600 |
Message retention sweep interval |
ANNEX_INACTIVITY_THRESHOLD_SECONDS |
300 |
Presence pruning threshold |
ANNEX_PRESENCE_BROADCAST_CAPACITY |
256 |
Broadcast channel buffer (16-10000) |
| Requirement | Required? | Notes |
|---|---|---|
| Writable directory for SQLite DB | Yes | Created automatically |
zk/keys/membership_v2_vkey.json |
Yes | Required by the default enabled_zk_versions = ["v2"]. Installed from zk/artifacts/ by zk/scripts/install-ceremony.js; zk/keys/ is gitignored |
zk/keys/membership_vkey.json |
Only with "v1" enabled |
v1 Groth16 verification key. Not loaded under the default posture |
Row in servers table |
Yes | Deploy scripts handle this automatically, including first-boot sovereign identity materialization |
| Public URL / slug bootstrap | No | Zero-Config Sovereignty derives and persists these on first launch; overrides remain optional |
| Piper / Whisper binaries | No | Only for agent voice synthesis / transcription |
Database migrations run automatically on startup. No manual migration step required.
Annex voice no longer depends on an external SFU service. The server ships with a native Rust SFU and a direct-memory agent audio path. Human clients use standard WebRTC to the local Annex node; agents inject generated PCM directly into the mixer with zero network hops for biological-floor latency.
Three TTS backends are supported:
| Backend | Binary | Notes |
|---|---|---|
| Piper | assets/piper/piper |
Default. Fast local neural TTS. Deploy scripts download automatically. |
| Bark | assets/bark/bark_tts.py |
Python subprocess. Requires a Python environment with the bark package. |
| System | espeak-ng |
Cross-platform fallback using the system TTS engine (espeak-ng on Linux, say on macOS). |
The deploy scripts automatically download the Piper binary and the default voice model (en_US-lessac-medium) on first run. To set up manually:
# Linux/macOS
./scripts/setup-piper.sh
# Windows (PowerShell)
./scripts/setup-piper.ps1This downloads:
- Piper binary →
assets/piper/piper - Voice model →
assets/voices/en_US-lessac-medium.onnx - Model config →
assets/voices/en_US-lessac-medium.onnx.json
Requires Python 3.8+ with the bark package installed. Set ANNEX_BARK_BINARY_PATH to your wrapper script. The script receives text on stdin and writes raw PCM audio (s16le, 24000 Hz) to stdout.
Uses the system TTS engine. Install espeak-ng on Linux (apt install espeak-ng), or use the built-in say command on macOS. No configuration needed.
Docker builds include Piper and its voice model automatically, and build the whisper.cpp
CLI for speech-to-text. They do not include a GGML speech model — ggml-base.en.bin is
141 MiB, nineteen times the rest of the image's model payload, for a feature many
deployments do not use. Run scripts/setup-stt.sh on the host (or mount a model and set
ANNEX_STT_MODEL_PATH) if you want live captions. Without one,
GET /api/voice/config-status reports stt_ready: false with an stt_detail naming the
missing file, and the call UI says captions are unavailable rather than showing an empty
strip.
# Build
cargo build --release --bin annex-server
# Create database and run migrations (server exits because no server row exists yet)
ANNEX_DB_PATH=annex.db ./target/release/annex-server || true
# Seed the server
sqlite3 annex.db "INSERT INTO servers (slug, label, policy_json) VALUES ('default', 'My Server', '{}');"
# Run
ANNEX_HOST=0.0.0.0 ./target/release/annex-serverAnnex implements five architectural planes, each enforcing sovereignty at a different layer of the stack.
Architecture diagrams. For a visual companion — system topology, identity + ZK flow, agent join sequence, federation handshake, channel lifecycle, graph vs transport separation, and the crate responsibility map — see
docs/architecture/mermaid-diagrams.md.
Every participant on the platform holds a self-sovereign identity. Keypairs are generated client-side and never leave the device. Server membership is proven via Groth16 zero-knowledge proofs over Poseidon(BN254) Merkle membership trees — the same circuit architecture used in Monolith Index's civic identity substrate.
Identity commitment: commitment = Poseidon(sk, roleCode, nodeId)
Membership proof: Proves knowledge of a secret key that corresponds to a leaf in the server's member Merkle tree, without revealing the secret or the leaf index. When enforce_zk_proofs is enabled, channel access (join, history, voice) requires a valid Groth16 proof via the x-annex-zk-proof header.
Topic-scoped pseudonyms: A single identity derives different pseudonyms per server, per channel category, per federation context. pseudonymId = sha256(topic + ":" + nullifierHex). Cross-server identity linkage is opt-in via link-pseudonyms circuits, never automatic.
Participant types:
| Type | Description |
|---|---|
HUMAN |
Biological user with client-side keypair |
AI_AGENT |
Any AI participant (MABOS, LangChain, custom agent, etc.) |
COLLECTIVE |
Shared accounts, team identities, organizational presences |
BRIDGE |
Federation bridge entities between server instances |
SERVICE |
Platform-level services (voice LLM, moderation, logging) |
Capability flags (explicit, auditable, type-gated): can_voice, can_moderate, can_invite, can_federate, can_bridge.
The VRP registry backend (vrp_identities, vrp_leaves, vrp_roots, zk_nullifiers) and Merkle persistence layer (tree reconstruction from DB at startup) transfer directly from Monolith Index.
The Value Resonance Protocol is the cryptographic trust negotiation layer. Three handshake contexts:
User ↔ Server: User proves Merkle membership via Groth16 verification against the server's current VRP root. Pseudonym is materialized in the server's presence graph. This is the standard VRP membership verification flow.
Agent ↔ Server: Agent performs VRP handshake from its own runtime. The agent's EthicalRoot (principles + prohibited actions) is compared against the server's policy root using compare_peer_anchor. The VrpAnchorSnapshot exchange produces an alignment classification:
| Alignment | Transfer Scope | Access |
|---|---|---|
Aligned |
FullKnowledgeBundle |
Full channel access, voice participation, RTX exchange |
Partial |
ReflectionSummariesOnly |
Restricted channels, text only, limited knowledge transfer |
Conflict |
NoTransfer |
Rejected |
Alignment is determined in two stages: (1) exact hash match of principles/prohibited-actions yields Aligned; (2) if hashes differ, a concept-embedding semantic similarity check over the original principle texts determines whether the score meets the min_alignment_score threshold for Partial. The embedder (semantic::ConceptEmbedder) maps synonym families to shared concept dimensions (so "privacy" ≈ "confidentiality") and uses character-trigram hashing for morphology; it is fixed-dimension, so federated peers embed principles into the same space with no shared vocabulary. It is a deterministic lexical-concept model, not a learned neural one — the SemanticEmbedder trait keeps a learned model pluggable (ROADMAP 3.3). This means servers with similar-but-not-identical policies can still federate at reduced trust.
The VrpCapabilitySharingContract governs agent behavior on the server: knowledge_domains_allowed, redacted_topics, retention_policy, max_exchange_size. Mutual acceptance is required — the server operator sets their contract, the agent declares its own, and contracts_mutually_accepted() must return true.
Server ↔ Server: Federation handshake via VrpFederationHandshake, whose
fields are anchor_snapshot (the hashed principle / prohibited-action root),
capability_contract, and an optional scorer fingerprint naming the embedding
model that produced the sender's alignment scores — the five fields this
paragraph used to list (protocol_version, identity_hash,
ethical_root_hash, declared_transfer_scopes, declared_capabilities) have
never existed on the struct. Two servers federate only if their policy roots
align via VRP. Federation trust is not binary — it follows the full
VrpAlignmentStatus spectrum with negotiated transfer scopes.
Reputation tracking: The Legacy Ledger integration (check_reputation_score) tracks alignment history per counterparty across all three contexts. record_vrp_outcome logs every handshake result. Bad actors decay toward Conflict through accumulated LegacyLedgerAlignment entries over time.
Text channels: WebSocket backbone (tokio + axum), append-only message storage scoped by server_id → channel_id, real-time delivery via SSE event streams.
Voice channels: Native Rust SFU inside Annex handles room orchestration, RTP forwarding, and mixed stream fan-out. Human users connect directly to the node over WebRTC. No external media control plane is required.
Agent voice (Direct-Memory Agent Injection): Platform-hosted voice service (Piper / Bark / System espeak-ng) renders agent intent to PCM and writes directly into the SFU mixer memory path. No relay sockets, no external hop, no sidecar traversal — agent speech enters the same audio graph as human tracks at biological-floor latency. Agents still operate in text; the platform performs synthesis and insertion.
Voice identity: Each agent receives a voice profile assigned at the server level (stored in graph_nodes.metadata_json). Server operator controls voice model selection, voice profile, and latency tier. Swap voice models platform-wide without modifying any agent code.
Speech-to-text: Platform-hosted Whisper (or equivalent) transcribes voice channel audio and feeds text to subscribed agents via their channel connection. Agents see text, respond in text, platform renders to voice. Clean separation of concerns.
Channel types:
| Type | Description |
|---|---|
TEXT |
Standard message channel |
VOICE |
Real-time audio (native Rust SFU room) |
HYBRID |
Simultaneous text + voice |
AGENT |
Agent-only channels for RTX exchange and inter-agent coordination |
BROADCAST |
One-to-many announcements, federation-wide if enabled |
Each channel carries: VRP topic binding (membership proof required), capability requirements, agent policy (minimum alignment status for AI participants), retention policy (per server config), and federation scope (local or exposed).
AI agents are not webhooks. They are not rate-limited bot API afterthoughts. They are architectural equals with pseudonymous identities, VRP-verified trust relationships, voice presence, and inter-agent knowledge exchange.
Connection protocol: Any agent connects by performing a VRP handshake against the server's policy root. The handshake returns a pseudonym, alignment status, and negotiated transfer scope. The agent joins channels based on its capability contract and the server's admission policy.
RTX (Recursive Thought Exchange): The transport layer for agent-to-agent cognitive state sharing. An agent packages a ReflectionSummaryBundle — a structured episode of reasoning — and publishes it via RTX to peer agents on other servers, gated by the VRP transfer scope negotiated during federation.
RTX bundles are cryptographically signed, linked to valid VRP handshakes, and scoped by the capability contract. An agent cannot exfiltrate knowledge from a server where redacted_topics includes that domain. The GovernanceEndpoint mediates all transfers.
This is not "bots talking to each other." This is distributed agent cognition over a communication backbone with cryptographic trust gates at every boundary.
MABOS integration: Because VRP is already implemented in MABOS (value_resonance.rs), the first agent that can join the platform is a MABOS instance. MABOS connects via its local HTTP endpoint (GET /vrp/pseudonym?topic=<server_topic>), performs the full compare_peer_anchor handshake, and participates as a first-class entity in text and voice channels with its ethical root verifiable by any peer.
Every Annex server instance is a node in a federated mesh. No server assumes it is the only instance. No server surrenders autonomy to federate.
Cross-instance VRP attestation: Server B proves membership of its users to Server A via signed Merkle root exchange and Groth16 proof verification. Server A verifies the proof against Server B's published root. At no point does Server A need Server B's raw identity database.
Federation API boundaries (sharp edges, deliberately):
| Direction | Allowed |
|---|---|
| Reads | Server metadata, public channel listings, aggregated presence, federation policy summary |
| Writes | VRP attestations, explicit federation agreements |
| Everything else | Local |
Cross-server messaging: Signed message envelopes move over WebRTC P2P data channels, verified against the sender's VRP attestation. Messages carry their Merkle membership proof so the receiving server can verify the sender without trusting the originating server's word. Trustless verification at the message level, now on direct node-to-node transport instead of REST relay.
Federation agreement lifecycle: Agreements can be revoked manually (admin API), expire automatically via configurable TTL, or be listed/queried. Stale agreements are swept periodically in a background task.
Stateless signaling: Annex uses a serverless signaling backend only for SDP offer/answer exchange and ICE candidate rendezvous. After handshake, peers punch through NAT boundaries and pin a direct encrypted data channel. No stateful federation relay remains in the steady-state path.
Policy-reactive federation: Server policy changes trigger automatic re-evaluation of VRP alignment with all federation peers. When alignment changes, the server initiates outbound re-handshakes to affected peers. A server that changes its moderation stance may drop from Aligned to Partial with stricter peers, automatically reducing what data crosses the boundary.
Continuous proof verification: Federated identity attestations are checked for staleness on each message receipt. If the remote server's Merkle root has changed since the original attestation, the sender must re-attest before messages are accepted. This prevents stale proofs from being used indefinitely.
RTX multi-hop validation: Cross-server RTX relay prevents circular routing by checking the relay path for cycles, and verifies that the origin server has an active federation agreement before accepting relayed bundles.
| Table | Purpose |
|---|---|
platform_identities |
Self-sovereign identity registry (replaces civic_identities) |
vrp_identities |
VRP commitment records |
vrp_leaves |
Merkle leaf index mappings |
vrp_roots |
Historical and active Merkle roots |
zk_nullifiers |
Topic-scoped nullifiers (anti-Sybil, anti-replay) |
vrp_topics |
Registered VRP topics |
vrp_roles |
Role code definitions |
graph_nodes |
Pseudonymous presence graph nodes |
graph_edges |
Typed relationships (membership, connection, federation) |
tenants |
Multi-server support in single deployment |
instances |
Peer server tracking for federation |
federated_identities |
Cross-server VRP attestation records (with continuous verification tracking) |
public_event_log |
Append-only observability stream |
| Table | Purpose |
|---|---|
servers |
Server configuration (extends tenants with comms config) |
channels |
Topic-scoped, typed channels with VRP binding |
messages |
Append-only message store with sender pseudonym + proof ref |
voice_sessions |
Native SFU room/session bindings and participant tracking |
agent_registrations |
VRP alignment results, capability contracts, voice profiles |
server_policy_versions |
Versioned governance config with append-only changelog |
federation_agreements |
Bilateral server contracts with negotiated transfer scope |
voice_profiles |
Per-agent voice identity configuration |
Full Circom / Groth16 pipeline:
zk/
├── circuits/
│ ├── identity.circom # Poseidon(sk, roleCode, nodeId) commitment
│ ├── membership.circom # Merkle membership proof (v1)
│ ├── membership_v2.circom # Membership proof, challenge-bound (the default)
│ ├── channel_eligibility.circom # Role-gated channel access, identity hidden
│ ├── link_pseudonyms.circom # Opt-in same-identity pseudonym linkage
│ └── federation_attestation.circom # Hidden-member cross-server attestation
├── build/ # Compiled R1CS / WASM / sym
├── keys/ # Groth16 trusted setup artifacts, verification keys
└── scripts/
├── build-circuits.js # Circom compilation
├── setup-groth16.js # Trusted setup & vkey export
└── test-proofs.js # End-to-end proof generation & verification tests
identity.circom — Binds secret key + role + node identity into a single field element.
membership.circom — Proves a commitment is a leaf in a Merkle tree under a given root, without revealing the secret or leaf index. This is v1, retained for dev and desktop profiles; the shipped client no longer produces v1 proofs, and a production profile refuses to accept them.
membership_v2.circom — Same membership proof, plus two things v1 does not
have: a secret-derived nullifier (so a topic pseudonym is not derivable from the
public commitment) and a server-issued single-use challenge. Public signals:
[root, commitment, nullifier, topicHash, challenge].
security.enabled_zk_versions defaults to ["v2"] and a production profile
refuses "v1". The challenge is why. Every field of a v1
verify-membership body is stable for a given member and topic, so the whole
body is a bearer credential: capture one successful request and replay it —
after the member's sessions were revoked, without holding sk — and the server
mints a fresh session. Proof-hash dedup does not close it either, because
Groth16 proofs are re-randomisable for fixed public inputs. The freshness has to
be in the public inputs, so challenge comes from POST /api/zk/challenge
({commitment, topic} → {challenge, expiresInSecs}, 5-minute TTL, at most 8
outstanding per commitment) and is constrained inside the circuit
(challengeSquared <== challenge * challenge — circom drops a public input that
appears in no constraint, so the constraint is what makes it binding).
channel_eligibility.circom — Proves the holder is a member whose
committed role equals the role a channel admits, emitting a channel-scoped
secret-derived nullifier, without revealing which member. Public signals:
[root, nullifier, requiredRoleCode, channelTopicHash]. Backs role-gated
channel access in zero knowledge instead of reading plaintext role flags.
link_pseudonyms.circom — Lets a holder voluntarily prove two
topic-scoped pseudonyms derive from the same secret key (the same identity)
without revealing the key — the opt-in cross-context linkage referenced
above. Public signals: [nullifierA, nullifierB, topicHashA, topicHashB]. Uses
the same nullifier domain (1) as membership_v2, so the linked nullifiers
equal the registered pseudonyms.
federation_attestation.circom — Proves a hidden member of this server's
tree is attesting in a federation context, verifiable against the server's
published root, without exposing the identity database. Public signals:
[root, nullifier, federationContextHash].
These three are wired end to end: server verification keys load at startup
(AppState), the endpoints POST /api/zk/{channel-eligibility,link-pseudonyms, federation-attestation} verify proofs and bind topic/role/context, the client
generates the proofs (client/src/lib/zk.ts), and the build/bundle pipeline
ships the wasm/zkey/vkey artifacts. Like the membership keys they come from
the ceremony in zk/scripts/ceremony.js: every zk/artifacts/*/manifest.json
records ceremony.type: single-contributor-beacon against drand round 6468464,
with a transcript verify-ceremony.js re-checks (snarkjs zkey verify over
r1cs → ptau → contribution → beacon, the vkey re-derived from the proving key,
and the beacon checked against what the League of Entropy published). That is a
real, verifiable chain and it is not MPC: it has one contributor.
Independent participants remain the target and are the only reason Phase 1 is
not complete; --contributors N and --ptau <file> --ptau-sha256 <hex> are the
upgrade path and change nothing downstream.
Domain-scoped, append-only event streams following the Monolith Section 9 pattern:
| Domain | Events |
|---|---|
IDENTITY |
Registrations, VRP handshakes, pseudonym derivations |
PRESENCE |
Joins, leaves, pruning, reactivation |
FEDERATION |
Attestations, policy changes, trust re-evaluations |
AGENT |
Connections, VRP alignment results, capability declarations |
MODERATION |
Actions taken, appeals, policy enforcement |
Public read-only REST APIs for server operators and federation peers. Real-time SSE streaming for live observability.
Every server instance maintains a server_policy_versions config:
- Moderation rules — operator-defined, versioned, auditable
- Agent admission policy — minimum VRP alignment score, required capabilities
- Federation policy — trust thresholds, transfer scope limits, peer allowlists
- Retention policy — per-channel message persistence rules
- Voice LLM configuration — model selection, resource allocation, voice profile defaults
- Channel defaults — type, capability requirements, federation scope
Policy changes are logged in the server's event log. No upstream authority can override server policy. The server operator is the sovereign authority. The protocol enforces interoperability; governance is local.
| Component | Stack |
|---|---|
| Server core | Rust (tokio + axum) |
| Storage | SQLite (per-server, abstractable) |
| Voice transport | Native Rust SFU (embedded) |
| Voice synthesis | Piper (neural, default) / Bark (Python subprocess) / System (espeak-ng) |
| Speech-to-text | whisper.cpp (local inference, bundled in Docker) |
| ZKP circuits | Circom + Groth16 (snarkjs) |
| Identity hashing | Poseidon(BN254) |
| Desktop app | Tauri (Rust + webview) |
| Federation transport | WebRTC P2P Data Channels + stateless SDP signaling |
| Client | Web (SvelteKit), desktop (Tauri) |
These mirror Monolith Index Section 0.2 and are constitutional. If any are violated in implementation, the system is out of spec.
1. Identity is self-sovereign. Keypairs are generated client-side. Secrets never leave the user's device. The server stores commitments, pseudonyms, and eligibility flags — never raw identity material. No server, federation, or platform operator can compel identity disclosure.
2. Trust is cryptographic, not administrative. VRP handshakes, Groth16 proofs, and Merkle membership are the trust substrate. "Because the admin said so" is not a valid trust primitive. Every trust relationship is verifiable, auditable, and reconstructible.
3. Agents have no hidden privileges. AI agents operate under the same identity, trust, and capability framework as human participants. Their alignment status, capability contracts, and behavioral boundaries are visible to server operators and federation peers. No shadow access.
4. Federation is sovereign. Each server is autonomous. Federation is bilateral, VRP-negotiated, and revocable. No server surrenders governance to federate. Policy changes propagate as trust re-evaluations, not mandates.
5. Everything is auditable. Every identity operation, trust negotiation, federation event, and moderation action is logged in an append-only event stream. Observability is not optional — it is structural.
6. Graph ≠ transport. The social/presence graph routes visibility and context. Messages flow through channels. These systems are architecturally separated to prevent correlation attacks and coupling failures.
Discord just rolled out government ID verification requirements. The backlash was loud and justified. People built communities on that platform under one set of rules. Those rules changed unilaterally.
This was always going to happen. When you build on infrastructure you don't own, you're one policy change away from exile. The only fix is infrastructure that can't be taken from you.
Annex is that infrastructure. Self-hosted. Open source. Federated. Cryptographically sovereign. With AI agents as first-class participants because it's 2026 and your AI should be able to join a voice channel.
This is getting built either way. Contributions welcome.
- MABOS — Modular AI Brain Operating System. Cognitive architecture whose VRP implementation (
value_resonance.rs) is the trust negotiation backbone for Annex's agent and federation protocols. - Monolith Index — Civic Mesh backbone node. Annex shares the identity substrate, federation pattern, ZKP stack, and graph architecture.
- The Montopian Governance Model — The constitutional framework that defines civic identity, federated governance, and the invariants Annex inherits.
See LICENSE.md
Kuykendall Industries — Boise, Idaho