Sibling to the Python verae-mcp-timestamping-server. Full tool parity, FileLedger, optional NATS (no JetStream for job-complete), official go-sdk Streamable HTTP, shadow on :8001 then cutover checklist.
6.3 KiB
Go MCP timestamping server
Date: 2026-09-29
Status: Draft for review
Sibling of: verae-mcp-timestamping-server (Python, live on ns1)
Contract: Same digests-only MCP tools as https://mcp.verae.ai/mcp and Application Client OpenAPI
Problem
The live Verae MCP timestamping service is a Python process (verae-mcp-timestamping-server in a venv, Streamable HTTP on 127.0.0.1:8000). Operators want a Go executable with full tool and ledger parity, suitable for shadow deploy and eventual cutover, without changing the public MCP contract.
Goals
- Single static Go binary implementing the MCP timestamping server.
- Full tool parity:
stamp,verify,hash_report,signup,login,session,create_user. - Local/shared
FileLedger(JSON file, mode 0600) and global VeraeTime HTTPS client. - Optional core-NATS bridge (same subjects as Python); no JetStream for job-complete.
- Streamable HTTP + stdio transports; CORS / allowed hosts/origins compatible with ns1.
- Shadow on ns1 beside Python; documented cutover checklist.
Non-goals (this project)
- Changing live MCP tool schemas or Application Client OpenAPI semantics.
- Replacing Python on day one without a shadow period.
- JetStream (or other persistent streams) for job-complete / jobId notification.
- Accepting file bytes / content on stamp tools.
Current state (Python)
- Package:
verae-mcp-timestamping-server(Python ≥3.10,mcp[cli]). - Deploy:
/opt/verae/verae-mcp-timestamping-server, userverae-mcp, systemdverae-mcp-timestamping.service. - Public: Caddy →
127.0.0.1:8000formcp.verae.ai,mcp.veraetime.net,mcp.georgelambert.org. - Global stamps:
POST https://api.veraetime.net/api/timestampwithrestamp: false.
Architecture
Clients
│
▼
verae-mcp-timestamping-server-go
├── local|shared → FileLedger (~/.verae/mcp-timestamp-local.json or VERAE_LEDGER_PATH)
├── global → api.veraetime.net (/api/timestamp, /api/verify, /api/hash/{sha}, /auth/*)
└── optional NATS → verae.ts.request, verae.ts.verify, verae.hash.report (core NATS only)
SDK: official github.com/modelcontextprotocol/go-sdk/mcp (NewStreamableHTTPHandler, AddTool, stdio).
Repository layout
Path: /Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server-go
Remote (planned): ssh://git@git.georgelambert.org:2223/marchon/verae-mcp-timestamping-server-go.git
cmd/verae-mcp-timestamping-server/main.go
internal/config/
internal/digest/
internal/ledger/ # FileLedger
internal/veraetime/ # GlobalClient + auth.properties
internal/natsbridge/ # optional
internal/tools/ # MCP tool handlers
docs/superpowers/specs/
docs/cutover.md # cutover checklist (deliverable)
README.md # Standard Readme
Binary name: verae-mcp-timestamping-server
Install dir on ns1 (shadow): /opt/verae/verae-mcp-timestamping-server-go/ so it does not overwrite the Python venv path.
Tool contract
Match Python docs/api.md / live tools/list:
| Tool | Required args | Notes |
|---|---|---|
stamp |
sha256 |
scope local|shared|global; optional cube_id, org_id |
verify |
receipt_id |
optional want_scope; global or CID-like id → VeraeTime |
hash_report |
sha256 |
optional org_id, want_scope |
signup |
username, password |
VeraeTime /auth/signup |
login |
username, password |
stores JWT in auth.properties; does not return JWT |
session |
— | /auth/validate |
create_user |
username, password, role |
admin bearer |
Digests only: reject content/bytes/file/data/path/body/payload/blob/archive.
Normalize SHA-256 (optional sha256: prefix; 64 hex).
Global stamp: restamp: false; poll /api/status/{jobId} when needed.
Config (env)
| Variable | Default |
|---|---|
VERAE_MCP_TRANSPORT |
streamable-http (or stdio) |
VERAE_MCP_HOST |
127.0.0.1 |
VERAE_MCP_PORT |
8000 (shadow uses 8001) |
VERAE_GLOBAL_URL |
https://api.veraetime.net |
VERAE_AUTH_PROPERTIES |
$HOME/.verae/auth.properties |
VERAE_LEDGER_PATH |
$HOME/.verae/mcp-timestamp-local.json |
VERAE_NATS_URL |
empty (optional) |
VERAE_MCP_CORS |
unset / 1 |
VERAE_MCP_ALLOWED_HOSTS |
same list as Python verae-ai.conf |
VERAE_MCP_ALLOWED_ORIGINS |
same list as Python |
HOME for the service user remains /var/lib/verae-mcp (shared auth file with Python during shadow if desired, or separate path for isolation).
Shadow deploy
- Build linux amd64 (and optionally arm64) release binary.
- Install under
/opt/verae/verae-mcp-timestamping-server-go/. - Systemd unit
verae-mcp-timestamping-go.service:VERAE_MCP_PORT=8001, same userverae-mcp, same or dedicated auth properties. - Optional Caddy path or internal-only listen for smoke (do not steal public
/mcpuntil cutover). - Compare
tools/listand stamp/verify/hash_report against Python on:8000.
Cutover checklist (docs/cutover.md)
- Shadow green for agreed soak (tools + global stamp/verify/hash_report + local ledger).
- Stop Python unit (or switch Caddy upstream).
- Point Go unit at
VERAE_MCP_PORT=8000(or Caddy to Go’s port). - Confirm
https://mcp.verae.ai/mcptools/list and a known digesthash_report. - Keep Python tree on disk for rollback for one release cycle.
- Rollback: restore Python
ExecStart/ Caddy upstream; restart.
Testing
- Unit: digest normalize/guard; FileLedger idempotency; VeraeTime request shaping (
restamp: false) with fake HTTP. - Integration: Streamable HTTP initialize → tools/call against an
httptesttest server. - Parity script: same digests against Python
:8000and Go:8001. - Optional live smoke behind flag (requires auth.properties).
Success criteria
go buildproduces a single runnable binary.- Full tool list matches Python.
- Local/shared persist to ledger file; global uses VeraeTime with caller/host token.
- Optional NATS path does not use JetStream for job-complete.
- Shadow unit runs on ns1 without disturbing Python
:8000. - Cutover doc is complete enough to swap without inventing steps on the day.
Open follow-ups
- Whether shadow shares
/var/lib/verae-mcp/auth.propertieswith Python or uses a separate file. - Whether public hostname gets a temporary Go-only path (e.g.
/mcp-go) during soak.