verae-mcp-timestamping-serv.../docs/superpowers/specs/2026-09-29-go-mcp-timestamping-server-design.md
George Lambert 7b5ae5b9aa docs: add pkg/tsnats NATS client to Go MCP design
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.
2026-09-29 11:40:47 -04:00

8.2 KiB
Raw Blame History

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

  1. Single static Go binary implementing the MCP timestamping server.
  2. Full tool parity: stamp, verify, hash_report, signup, login, session, create_user.
  3. Local/shared FileLedger (JSON file, mode 0600) and global VeraeTime HTTPS client.
  4. Optional core-NATS bridge (same subjects as Python); no JetStream for job-complete.
  5. Streamable HTTP + stdio transports; CORS / allowed hosts/origins compatible with ns1.
  6. Shadow on ns1 beside Python; documented cutover checklist.
  7. Importable pkg/tsnats NATS 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, user verae-mcp, systemd verae-mcp-timestamping.service.
  • Public: Caddy → 127.0.0.1:8000 for mcp.verae.ai, mcp.veraetime.net, mcp.georgelambert.org.
  • Global stamps: POST https://api.veraetime.net/api/timestamp with restamp: 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

  1. Build linux amd64 (and optionally arm64) release binary.
  2. Install under /opt/verae/verae-mcp-timestamping-server-go/.
  3. Systemd unit verae-mcp-timestamping-go.service: VERAE_MCP_PORT=8001, same user verae-mcp, same or dedicated auth properties.
  4. Optional Caddy path or internal-only listen for smoke (do not steal public /mcp until cutover).
  5. Compare tools/list and stamp/verify/hash_report against Python on :8000.

Cutover checklist (docs/cutover.md)

  1. Shadow green for agreed soak (tools + global stamp/verify/hash_report + local ledger).
  2. Stop Python unit (or switch Caddy upstream).
  3. Point Go unit at VERAE_MCP_PORT=8000 (or Caddy to Go’s port).
  4. Confirm https://mcp.verae.ai/mcp tools/list and a known digest hash_report.
  5. Keep Python tree on disk for rollback for one release cycle.
  6. 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 :8000 and Go :8001.
  • Optional live smoke behind flag (requires auth.properties).

Success criteria

  • go build produces 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/tsnats builds 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.properties with Python or uses a separate file.
  • Whether public hostname gets a temporary Go-only path (e.g. /mcp-go) during soak.