52 lines
3.2 KiB
Markdown
52 lines
3.2 KiB
Markdown
# MCP revocation delivery (default off)
|
|
|
|
This capability is not enabled by adding the code. Deploy and verify the MM v2
|
|
receiver first, using the existing separate `MCP_PORTAL_WEBHOOK_SECRET`. Then
|
|
explicitly configure portal:
|
|
|
|
- `MCP_REVOCATIONS_ENABLED=true`
|
|
- `MCP_REVOCATIONS_TARGET=https://<fixed-mm-host>/_internal/mcp/portal/revocations`
|
|
- `MCP_REVOCATIONS_SECRET`: the same separate random secret, at least32 characters.
|
|
|
|
Never reuse JWT or audit-ingest secrets. Do not put credentials/query/fragment in
|
|
the target. Only canonical HTTPS is accepted, with certificate verification,
|
|
no redirects and a3-second whole request deadline; request/response limit4KiB.
|
|
The target fingerprint is pinned in SQLite; changing destination refuses startup
|
|
until an explicit operator-reviewed migration. Rotation of the shared secret
|
|
does not change queued event bodies or IDs.
|
|
|
|
Portal SQLite migration2 adds an outbox without a user FK. When enabled, SQLite
|
|
synchronous=FULL is required before mutations. Actual access/admin/disable/delete,
|
|
logout, single-session and revoke-all functions enqueue in their same synchronous
|
|
transaction. Enqueue failure/cap4096 rolls back the mutation. Queue-full prevents
|
|
further relevant security mutations until delivery recovers; rows are never
|
|
silently expired/discarded. Default-off neither captures new events nor sends;
|
|
it does not promise revocation while disabled. Existing pending rows remain.
|
|
|
|
Delivery is at least once, FIFO, one request at a time, at most4 events per tick;
|
|
retry delay grows to60seconds. A valid200 `{ok:true,duplicate:boolean}` ack removes
|
|
the exact row/body. Crash after MM commit but before ack causes safe redelivery
|
|
with the same eventId/body. Sender shutdown aborts and awaits request close before
|
|
SQLite closes. Logs contain only a fixed unavailable event, never secret/body/cause.
|
|
|
|
V2 signs `timestamp + "." + exactQueuedBody` with HMAC-SHA256. `x-mm-timestamp`
|
|
is fresh delivery time, while body `eventOccurredAt` is the immutable original
|
|
mutation time. Re-signing/retrying never advances the owner cutoff. MM applies
|
|
monotonic owner cutoffs and connection/credential/grant/plan/profile effects only
|
|
to claims issued at or before that event. Legacy three-field events remain
|
|
accepted with their historical signed-timestamp cutoff; this sender always usesv2.
|
|
|
|
Legacy JWT iat has second resolution. A JWT minted in the event's same second can
|
|
remain rejected; obtain a fresh login after the natural second boundary. No future
|
|
iat, artificial millisecond JWT precision or claims-version authority is added.
|
|
Logout/single-session revoke is owner-wide for MCP because current portal access
|
|
JWTs have no session identifier. Password-only/name/email edits retain their
|
|
existing semantics; this capability does not invent a new password/session policy.
|
|
|
|
Before enabling: use portal's locked dependencies for targeted DB tests and API/DB
|
|
typecheck, then verify actual HTTPS and MM PostgreSQL behavior (old JWT rejected,
|
|
fresh JWT after boundary preserved by late/out-of-order events, replay/conflict,
|
|
outage/restart/drain). Source-only and mock-network tests are not that acceptance.
|
|
SQLite backup restore is an explicit operator security operation; an outbox cannot
|
|
recover mutations deliberately removed by restoring an older database snapshot.
|