MCP remains the AI tool surface. Importable pkg/tsnats covers Stamp, Verify, HashReport over core NATS plus SubscribeCompleted; MCP optional upstream reuses the same package. No JetStream for job-complete.
8.2 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.
- Importable
pkg/tsnatsNATS client for bulk app workers (MCP stays the AI path).
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
AI assistants ──MCP (stdio / Streamable HTTP)──► verae-mcp-timestamping-server-go
├── FileLedger (local|shared)
├── VeraeTime HTTPS (global)
└── optional upstream NATS (same pkg/tsnats)
Bulk apps / bus workers ──pkg/tsnats──► NATS core
request/reply: verae.ts.request | verae.ts.verify | verae.hash.report
notify (subscribe): verae.ts.completed / receipt-style subjects
(no JetStream for job-complete)
Split: MCP tools remain the AI surface. pkg/tsnats is the digests-only messaging client for large concurrent stamp batches and async completion.
SDK: official github.com/modelcontextprotocol/go-sdk/mcp (NewStreamableHTTPHandler, AddTool, stdio).
NATS: github.com/nats-io/nats.go inside pkg/tsnats (and reused by internal/natsbridge).
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
pkg/tsnats/ # importable NATS timestamp client (Stamp/Verify/HashReport + Completed)
internal/config/
internal/digest/
internal/ledger/ # FileLedger
internal/veraetime/ # GlobalClient + auth.properties
internal/natsbridge/ # optional MCP upstream; wraps pkg/tsnats
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.
pkg/tsnats (bulk app client)
Import path (planned): git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/pkg/tsnats
| API | Behavior |
|---|---|
Connect(url string, opts ...Option) (*Client, error) |
Core NATS connection |
Stamp(ctx, sha256, opts) |
Request/reply on verae.ts.request; digests only |
Verify(ctx, receiptID, opts) |
verae.ts.verify |
HashReport(ctx, sha256, opts) |
verae.hash.report |
SubscribeCompleted(ctx, handler) |
Core subscribe to verae.ts.completed (and documented receipt alias if any); not JetStream |
Defaults: request timeout ~2s (configurable); concurrent Stamp calls are safe; empty/invalid digests fail client-side before publish.
The MCP server’s optional NATS upstream (internal/natsbridge) imports this package so bus encoding stays one implementation.
Bulk guidance: prefer many in-flight Stamp calls (or fire request + SubscribeCompleted) over serial MCP tools/call. For central global certificates when not on the bus, Application Client / VeraeTime HTTPS remains valid.
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 httptest test 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.
pkg/tsnatsbuilds as an importable library with unit tests against a NATS test server or fake conn.- 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.