avp

Alt Vault Protocol, an open zero-knowledge spec for sharing alts across clients.

AVP reference server, Go (in-memory)

An in-memory reference server for the Alt Vault Protocol, HTTP/JSON profile. It implements the whole wire contract so an implementer can point a client at something real. It is a sibling of the Rust reference server and behaves identically.

Illustrative, not for production. State lives in process memory and is lost on restart, there is no TLS, and the bearer token is an opaque random string mapped to a member id in this same process. A real deployment mints a JWT verifiable via JWKS (SPEC section 3) and serves over HTTPS. What this server does honour is the part that matters: it is zero-knowledge. It stores only the manifest, the encrypted envelope, the per-member wrapped keys, public keys, and the version/epoch counters that clients send, and it decrypts nothing. The only cryptography it performs is verifying the Ed25519 challenge signature.

Build and run

Requires Go 1.22+. No dependencies (standard library only).

go run .             # listens on http://localhost:8787
PORT=9000 go run .   # override the port
go build             # build the ./server binary
go test ./...        # run the smoke test

Endpoints

All bodies are JSON using the proto field names in camelCase (see ../../../schema/avp.schema.json). Every route except the two auth routes requires Authorization: Bearer <token>.

Method + path Request → Response
POST /api/auth/keypair/challenge { ed25519PublicKey }{ nonce } (single-use, ~2 min TTL)
POST /api/auth/keypair/token { ed25519PublicKey, nonce, signature }{ token, expiresAt }
POST /v1/repos { manifest, initialEnvelope }VaultManifest
POST /v1/repos/{repoId}/pull { repoId, knownPayloadVersion }{ manifest, envelope?, unchanged }
POST /v1/repos/{repoId}/push { repoId, envelope, expectedPayloadVersion, rotatedMembers? }{ accepted, conflict, payloadVersion, keyEpoch }
POST /v1/repos/{repoId}/add-member { repoId, member }VaultManifest
POST /v1/repos/{repoId}/remove-member { repoId, removedMemberId, rotatedEnvelope, rewrappedMembers, newKeyEpoch }VaultManifest
GET /v1/repos/{repoId}/member/{memberId} MemberEntry (URL-decode memberId; base64 ids contain + / =)

Status codes: 401 (missing/unknown token, or a bad/expired challenge), 403 (authenticated caller is not a member of the repo), 404 (unknown repo or member), 409 (duplicate repoId), 200 otherwise.

Notes for implementers

Message shapes are typed structs (../avp/wire.go), but the cryptographic fields (wrapped keys, IVs, ciphertext) are plain base64 strings the server stores and echoes back verbatim without interpreting, which keeps it faithful to the zero-knowledge guarantee. Routing uses the request’s escaped path so a percent-encoded slash inside a base64 member id is not mistaken for a path separator.

License

MIT (SPDX-License-Identifier: MIT).