Test coverage for the entire ACME server implementation:
- helpers_test.go: memBarrier, key generation, JWS/EAB signing, test fixtures
- nonce_test.go: issue/consume lifecycle, reuse rejection, concurrency
- jws_test.go: JWS parsing/verification (ES256, ES384, RS256), JWK parsing,
RFC 7638 thumbprints, EAB HMAC verification, key authorization
- eab_test.go: EAB credential CRUD, account/order listing
- validate_test.go: HTTP-01 challenge validation with httptest servers,
authorization/order state machine transitions
- handlers_test.go: full ACME protocol flow via chi router — directory,
nonce, account creation with EAB, order creation, authorization retrieval,
challenge triggering, finalize (order-not-ready), cert retrieval/revocation,
CSR identifier validation
One production change: extract dnsResolver variable in validate.go for
DNS-01 test injection (no behavior change).
All 60 tests pass with -race. Full project vet and test clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The web UI connects to the vault API via gRPC using the Docker
compose service name (e.g., "metacrypt:9443"), but the vault's TLS
certificate has SANs for "crypt.metacircular.net" and "localhost".
The new vault_sni config field overrides the TLS ServerName so
certificate verification succeeds despite the hostname mismatch.
Also updates metacrypt-rift.toml with vault_sni and temporarily
binds the web UI port to 0.0.0.0 for direct access until mc-proxy
is deployed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Run containers as user 0:0 (root inside container = kyle on host
via rootless podman UID mapping). This allows the container process
to read /srv/metacrypt/ files owned by kyle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- db.Open: delegate to mcdsl/db.Open
- db.Migrate: convert to mcdsl/db.Migration format, delegate
- auth: type aliases for TokenInfo/Authenticator/Config from mcdsl,
re-export error sentinels, Logout helper
- cmd/server: construct auth.Authenticator from Config (not mcias.Client)
- server/routes.go logout: use auth.Logout(authenticator, token)
- grpcserver/auth.go: same logout pattern, fix Login return type
(time.Time not string)
- webserver: replace mcias.Client with mcdsl/auth for service token
validation; resolveUser degrades to raw UUID (TODO: restore when
mcias client library is properly tagged)
- Dockerfiles: bump to golang:1.25-alpine, remove gcc/musl-dev,
add VERSION build arg
- Deploy: add docker-compose-rift.yml with localhost-only port mapping
- Remove git.wntrmute.dev/kyle/mcias/clients/go dependency entirely
- All tests pass, net -185 lines
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both deploy/examples configs now document the new server.grpc_addr and
[web] settings introduced with the metacrypt-web separation. The Docker
example uses the compose service name (metacrypt:9443) as vault_grpc.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The vault server holds in-memory unsealed state (KEK, engine keys) that
is lost on restart, requiring a full unseal ceremony. Previously the web
UI ran inside the vault process, so any UI change forced a restart and
re-unseal.
This change extracts the web UI into a separate metacrypt-web binary
that communicates with the vault over an authenticated gRPC connection.
The web server carries no sealed state and can be restarted freely.
- gen/metacrypt/v1/: generated Go bindings from proto/metacrypt/v1/
- internal/grpcserver/: full gRPC server implementation (System, Auth,
Engine, PKI, Policy, ACME services) with seal/auth/admin interceptors
- internal/webserver/: web server with gRPC vault client; templates
embedded via web/embed.go (no runtime web/ directory needed)
- cmd/metacrypt-web/: standalone binary entry point
- internal/config: added [web] section (listen_addr, vault_grpc, etc.)
- internal/server/routes.go: removed all web UI routes and handlers
- cmd/metacrypt/server.go: starts gRPC server alongside HTTP server
- Deploy: Dockerfile builds both binaries, docker-compose adds
metacrypt-web service, new metacrypt-web.service systemd unit,
Makefile gains proto/metacrypt-web targets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add the first concrete engine implementation: a CA (PKI) engine that generates
a self-signed root CA at mount time, issues scoped intermediate CAs ("issuers"),
and signs leaf certificates using configurable profiles (server, client, peer).
Engine framework updates:
- Add CallerInfo struct for auth context in engine requests
- Add config parameter to Engine.Initialize for mount-time configuration
- Export Mount.Engine field; add GetEngine/GetMount on Registry
CA engine (internal/engine/ca/):
- Two-tier PKI: root CA → issuers → leaf certificates
- 10 operations: get-root, get-chain, get-issuer, create/delete/list issuers,
issue, get-cert, list-certs, renew
- Certificate profiles with user-overridable TTL, key usages, and key algorithm
- Private keys never stored in barrier; zeroized from memory on seal
- Supports ECDSA, RSA, and Ed25519 key types via goutils/certlib/certgen
Server routes:
- Wire up engine mount/request handlers (replace Phase 1 stubs)
- Add public PKI routes (/v1/pki/{mount}/ca, /ca/chain, /issuer/{name})
for unauthenticated TLS trust bootstrapping
Also includes: ARCHITECTURE.md, deploy config updates, operational tooling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>