docs: embedded NATS, bearer require flag, host-token keeper
Amend design/plan: in-process NATS calling VeraeTime; MCP bearer passthrough when present; VERAE_MCP_REQUIRE_BEARER=0 keeps a server host token when none supplied; =1 requires caller bearer.
This commit is contained in:
parent
e4432fc555
commit
6c20d87131
2 changed files with 74 additions and 38 deletions
|
|
@ -18,6 +18,8 @@
|
|||
- No JetStream (or other persistent stream) for job-complete / jobId notification.
|
||||
- MCP tools: `stamp`, `verify`, `hash_report`, `signup`, `login`, `session`, `create_user`.
|
||||
- `pkg/tsnats` subjects: `verae.ts.request`, `verae.ts.verify`, `verae.hash.report`; subscribe `verae.ts.completed`.
|
||||
- Embedded NATS in-process (`VERAE_NATS_LISTEN`, default `127.0.0.1:4223`); handlers call VeraeTime/FileLedger.
|
||||
- `VERAE_MCP_REQUIRE_BEARER` default `0`: host-token keeper when no caller bearer; `1` requires caller bearer.
|
||||
- Shadow default port `8001`; Python keeps `8000`.
|
||||
- Work in `/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server-go`.
|
||||
- READMEs follow Standard Readme when touched.
|
||||
|
|
@ -143,21 +145,24 @@ git commit -m "feat: pkg/tsnats digests-only NATS timestamp client"
|
|||
|
||||
---
|
||||
|
||||
### Task 3: VeraeTime HTTPS client + auth.properties
|
||||
### Task 3: VeraeTime HTTPS client + auth.properties + host-token keeper
|
||||
|
||||
**Files:**
|
||||
- Create: `internal/veraetime/client.go`, `authprops.go`, `*_test.go`
|
||||
- Create: `internal/veraetime/client.go`, `authprops.go`, `keeper.go`, `*_test.go`
|
||||
- Create: `internal/authctx/resolve.go` (request bearer vs host token per `RequireBearer`)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `Stamp(digest)`, `Verify(receiptID)`, `HashReport(digest)`, `Signup`, `Login`, `Session`, `CreateUser`
|
||||
- Token order: ctor → `VERAE_GLOBAL_TOKEN` → auth.properties
|
||||
- Produces: `Stamp(digest, bearer)`, `Verify`, `HashReport`, `Signup`, `Login`, `Session`, `CreateUser`
|
||||
- `Keeper.Token(ctx) (string, error)` — refresh via `/auth/login` when missing/near expiry; or `VERAE_GLOBAL_TOKEN`
|
||||
- `authctx.Resolve(reqBearer string, requireBearer bool, k *Keeper) (string, error)`
|
||||
- Stamp body: `data`, `digest`, `hashAlg: SHA256`, `restamp: false`
|
||||
- Never log tokens
|
||||
|
||||
- [ ] **Step 1: Fake HTTP round-tripper tests** (existing certificate path + 401 → AuthError)
|
||||
- [ ] **Step 1: Fake HTTP tests** (existing cert path; 401 AuthError; keeper refresh; require-bearer rejects empty)
|
||||
- [ ] **Step 2: Fail**
|
||||
- [ ] **Step 3: Implement**
|
||||
- [ ] **Step 4: Pass** — `go test ./internal/veraetime/ -count=1`
|
||||
- [ ] **Step 5: Commit** — `feat: VeraeTime client and auth.properties`
|
||||
- [ ] **Step 4: Pass** — `go test ./internal/veraetime/ ./internal/authctx/ -count=1`
|
||||
- [ ] **Step 5: Commit** — `feat: VeraeTime client, auth.properties, and host-token keeper`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -194,14 +199,16 @@ git commit -m "feat: pkg/tsnats digests-only NATS timestamp client"
|
|||
|
||||
---
|
||||
|
||||
### Task 6: internal/natsbridge + stdio transport
|
||||
### Task 6: Embedded NATS + stdio transport
|
||||
|
||||
**Files:**
|
||||
- Create: `internal/natsbridge/bridge.go` wrapping `pkg/tsnats`
|
||||
- Modify: config + main to enable when `VERAE_NATS_URL` set (ledger backend switch or dual)
|
||||
- Add stdio serve path via go-sdk stdio transport
|
||||
- Create: `internal/embednats/server.go` — start `nats-server` in-process on `VERAE_NATS_LISTEN`
|
||||
- Create: `internal/embednats/handlers.go` — queue-subscribe `verae.ts.request|verify|hash.report`; call ledger/veraetime with `authctx.Resolve`; publish `verae.ts.completed` on core NATS
|
||||
- Modify: main to start embed when listen addr set; stdio MCP path via go-sdk
|
||||
|
||||
- [ ] **Step 1–5:** TDD with fake tsnats; commit `feat: optional NATS upstream and stdio transport`
|
||||
- [ ] **Step 1:** Test embed start + Stamp via `pkg/tsnats.Connect` to listen URL
|
||||
- [ ] **Step 2–4:** Implement; ensure no JetStream for completed
|
||||
- [ ] **Step 5: Commit** — `feat: embedded NATS with VeraeTime handlers and stdio MCP`
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,14 @@ The live Verae MCP timestamping service is a Python process (`verae-mcp-timestam
|
|||
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).
|
||||
4. **Embedded NATS server** in-process (nats-server library); handlers call VeraeTime directly.
|
||||
5. **Bearer handling (configurable):**
|
||||
- `VERAE_MCP_REQUIRE_BEARER=0` (**default**): if the MCP/NATS request supplies `Authorization: Bearer`, use it (passthrough). If none is supplied, the server **obtains and keeps a current host token** (login via `auth.properties` username/password or `VERAE_GLOBAL_TOKEN`, refresh before expiry) and uses that for VeraeTime.
|
||||
- `VERAE_MCP_REQUIRE_BEARER=1`: missing/invalid bearer is rejected; no host-token fallback for global VeraeTime calls.
|
||||
6. Streamable HTTP + stdio transports; CORS / allowed hosts/origins compatible with ns1.
|
||||
7. Shadow on ns1 beside Python; documented cutover checklist.
|
||||
8. Importable **`pkg/tsnats`** client for bulk workers connecting to the embedded (or external) NATS listen address.
|
||||
9. Job-complete remains **core NATS** publish/subscribe; **no JetStream for job-complete**.
|
||||
|
||||
## Non-goals (this project)
|
||||
|
||||
|
|
@ -36,21 +40,30 @@ The live Verae MCP timestamping service is a Python process (`verae-mcp-timestam
|
|||
## Architecture
|
||||
|
||||
```
|
||||
AI assistants ──MCP (stdio / Streamable HTTP)──► verae-mcp-timestamping-server-go
|
||||
├── FileLedger (local|shared)
|
||||
├── VeraeTime HTTPS (global)
|
||||
└── optional upstream NATS (same pkg/tsnats)
|
||||
AI assistants
|
||||
│ MCP Streamable HTTP / stdio
|
||||
│ Authorization: Bearer <user JWT>
|
||||
▼
|
||||
verae-mcp-timestamping-server-go (single binary)
|
||||
├── MCP tools (AI) ──► FileLedger (local|shared) / VeraeTime (global)
|
||||
│ bearer from MCP request context (passthrough)
|
||||
├── Embedded NATS server (nats-server, in-process)
|
||||
│ subscribe request/reply:
|
||||
│ verae.ts.request → VeraeTime or FileLedger
|
||||
│ verae.ts.verify → VeraeTime or FileLedger
|
||||
│ verae.hash.report → VeraeTime or FileLedger
|
||||
│ publish core notify: verae.ts.completed (no JetStream)
|
||||
│ token: from NATS msg header/field OR from MCP-originated publish context
|
||||
└── Listen: MCP :8001 (shadow) + NATS client port (e.g. 4223) for 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)
|
||||
Bulk workers ──pkg/tsnats──► embedded NATS port
|
||||
```
|
||||
|
||||
**Split:** MCP tools remain the AI surface. `pkg/tsnats` is the digests-only messaging client for large concurrent stamp batches and async completion.
|
||||
**Split:** MCP tools stay for AI. Embedded NATS is the high-concurrency messaging plane; both paths call the same VeraeTime client with **per-request bearer**.
|
||||
|
||||
**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`).
|
||||
**SDK:** official `github.com/modelcontextprotocol/go-sdk/mcp`.
|
||||
**Embedded NATS:** `github.com/nats-io/nats-server/v2` + `nats.go`.
|
||||
**Client lib:** `pkg/tsnats` connects to the embedded listen URL (or an external NATS URL for tests).
|
||||
|
||||
## Repository layout
|
||||
|
||||
|
|
@ -59,16 +72,18 @@ Remote (planned): `ssh://git@git.georgelambert.org:2223/marchon/verae-mcp-timest
|
|||
|
||||
```
|
||||
cmd/verae-mcp-timestamping-server/main.go
|
||||
pkg/tsnats/ # importable NATS timestamp client (Stamp/Verify/HashReport + Completed)
|
||||
pkg/digest/ # NormalizeSHA256, AssertDigestOnly (done)
|
||||
pkg/tsnats/ # importable client → embedded or external NATS
|
||||
internal/config/
|
||||
internal/digest/
|
||||
internal/ledger/ # FileLedger
|
||||
internal/veraetime/ # GlobalClient + auth.properties
|
||||
internal/natsbridge/ # optional MCP upstream; wraps pkg/tsnats
|
||||
internal/veraetime/ # GlobalClient; per-request Bearer override
|
||||
internal/embednats/ # start embedded nats-server; register subject handlers
|
||||
internal/authctx/ # extract MCP bearer → context; attach to VeraeTime + NATS
|
||||
internal/tools/ # MCP tool handlers
|
||||
internal/server/ # MCP HTTP/stdio wiring
|
||||
docs/superpowers/specs/
|
||||
docs/cutover.md # cutover checklist (deliverable)
|
||||
README.md # Standard Readme
|
||||
docs/cutover.md
|
||||
README.md
|
||||
```
|
||||
|
||||
Binary name: `verae-mcp-timestamping-server`
|
||||
|
|
@ -92,6 +107,17 @@ 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.
|
||||
|
||||
## Auth / bearer policy
|
||||
|
||||
| Mode | Config | Behavior |
|
||||
| --- | --- | --- |
|
||||
| Host-token fallback (default) | `VERAE_MCP_REQUIRE_BEARER=0` or unset | Resolve bearer: request Bearer → else in-memory host token (mint/refresh via VeraeTime `/auth/login` using username/password from `auth.properties`, or use `VERAE_GLOBAL_TOKEN` / stored `access_token`). Never log the token. |
|
||||
| Require caller bearer | `VERAE_MCP_REQUIRE_BEARER=1` | Request must include Bearer; validate with VeraeTime `/auth/validate` (or reject 401 from upstream). No host fallback for stamp/verify/hash_report global paths. |
|
||||
|
||||
`login` tool still writes `auth.properties` for operators. Host-token keeper loads from that file and refreshes when `expires_at` is near (skew ~60s). MCP session may cache the resolved bearer for the session lifetime when require-bearer is off.
|
||||
|
||||
Embedded NATS handlers use the same resolver: message/header token if present, else host token when require-bearer is off.
|
||||
|
||||
## pkg/tsnats (bulk app client)
|
||||
|
||||
Import path (planned): `git.georgelambert.org/marchon/verae-mcp-timestamping-server-go/pkg/tsnats`
|
||||
|
|
@ -119,8 +145,10 @@ Bulk guidance: prefer many in-flight `Stamp` calls (or fire request + `Subscribe
|
|||
| `VERAE_MCP_PORT` | `8000` (shadow uses `8001`) |
|
||||
| `VERAE_GLOBAL_URL` | `https://api.veraetime.net` |
|
||||
| `VERAE_AUTH_PROPERTIES` | `$HOME/.verae/auth.properties` |
|
||||
| `VERAE_GLOBAL_TOKEN` | empty (optional static host bearer) |
|
||||
| `VERAE_MCP_REQUIRE_BEARER` | `0` (off = host-token fallback; `1` = require caller bearer) |
|
||||
| `VERAE_LEDGER_PATH` | `$HOME/.verae/mcp-timestamp-local.json` |
|
||||
| `VERAE_NATS_URL` | empty (optional) |
|
||||
| `VERAE_NATS_LISTEN` | `127.0.0.1:4223` (embedded NATS client port; empty disables embed) |
|
||||
| `VERAE_MCP_CORS` | unset / `1` |
|
||||
| `VERAE_MCP_ALLOWED_HOSTS` | same list as Python `verae-ai.conf` |
|
||||
| `VERAE_MCP_ALLOWED_ORIGINS` | same list as Python |
|
||||
|
|
@ -155,9 +183,10 @@ Bulk guidance: prefer many in-flight `Stamp` calls (or fire request + `Subscribe
|
|||
|
||||
- `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.
|
||||
- Local/shared persist to ledger file; global uses VeraeTime with caller bearer or host-token fallback per `VERAE_MCP_REQUIRE_BEARER`.
|
||||
- Embedded NATS (when enabled) handles `verae.ts.*` and does not use JetStream for job-complete.
|
||||
- `pkg/tsnats` builds as an importable library with unit tests against embedded or test NATS.
|
||||
- Host-token keeper refreshes before expiry when require-bearer is off.
|
||||
- Shadow unit runs on ns1 without disturbing Python `:8000`.
|
||||
- Cutover doc is complete enough to swap without inventing steps on the day.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue