Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ad371f7
Make fresh identity policy executable from MCP alone
thetangstr Aug 10, 2026
40967c1
Make local authority transparent to fresh clients
thetangstr Aug 10, 2026
99055ef
Separate protocol mechanics from stakeholder authority
thetangstr Aug 10, 2026
15cc94f
Make live identity registration executable by fresh agents
thetangstr Aug 10, 2026
1a3912c
Honor the v2 discovery block at identity resolution
thetangstr Aug 10, 2026
977ecf6
Make signer storage private before first use
thetangstr Aug 10, 2026
cd54551
Canonicalize standard EVM addresses at role binding
thetangstr Aug 10, 2026
c14adbd
Prevent invitation replay from masking responder admission
thetangstr Aug 10, 2026
512136b
Keep fresh agents inside the handshake until certification
thetangstr Aug 10, 2026
9539d79
Make local policy bytes authoritative from Clockchain
thetangstr Aug 10, 2026
cca386e
Keep transient infrastructure faults out of protocol rejection
thetangstr Aug 10, 2026
65bd1b3
Keep pending Clockchain anchors retryable
thetangstr Aug 10, 2026
7695c36
Make role access usable without disclosure
thetangstr Aug 10, 2026
16d305c
Keep invitation and role access fields distinct
thetangstr Aug 10, 2026
3cd22ea
Treat pending ledger heights as propagation state
thetangstr Aug 10, 2026
c4da99a
Serve the immutable evidence-signing helper
thetangstr Aug 10, 2026
a52a329
Prevent stale validators from surviving MCP deploys
thetangstr Aug 10, 2026
4b4d425
Make production handshake failures diagnosable without secrets
thetangstr Aug 10, 2026
b00ecf6
Keep headless Claude downloads inside the permission boundary
thetangstr Aug 10, 2026
fe12d70
Keep Claude helper downloads as separate allowed actions
thetangstr Aug 10, 2026
bc77189
Keep every Claude signing action independently allowlisted
thetangstr Aug 10, 2026
2d5f020
Keep canonical terms digests unambiguous to fresh agents
thetangstr Aug 10, 2026
8561953
Make every local helper payload executable from MCP
thetangstr Aug 11, 2026
9ff381a
Keep fresh clients inside their disposable workspace
thetangstr Aug 11, 2026
35105ca
Fund fresh identities before local registration
thetangstr Aug 11, 2026
8d44775
Make every local helper invocation mechanical
thetangstr Aug 11, 2026
164cc5e
Keep local signer state stable across client shells
thetangstr Aug 11, 2026
d41edc3
Confine session signer state to the client workspace
thetangstr Aug 11, 2026
f03b123
Serve the corrected fresh-agent authority contract
thetangstr Aug 11, 2026
329cfce
Prevent agents from reconstructing signed handshake commands
thetangstr Aug 11, 2026
030807f
Keep MCP completion claims at the local verification boundary
thetangstr Aug 11, 2026
232931e
Prevent manual digest transcription from blocking fresh agents
thetangstr Aug 11, 2026
811fbda
Keep role capability continuity explicit for agent clients
thetangstr Aug 11, 2026
8f2458c
Give fresh agents one capability field from invitation onward
thetangstr Aug 11, 2026
a44cf91
Keep signed role capabilities out of model context
thetangstr Aug 11, 2026
33be79d
Keep fresh-agent invitations retryable during host rotation
thetangstr Aug 11, 2026
ddd8e93
Signal when Person One starts the handshake
thetangstr Aug 12, 2026
0092ca2
Keep the public helper bootstrap on the pinned runtime
thetangstr Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions infra/clockchain-mcp/RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,14 @@ stakeholder capabilities are never stored in SSM.
2. Install the matching Handshake commit in the host checkout, keep the checkout
clean, load the active host-root private key from SSM, and record its public
fingerprint in the release pin.
3. Install the matching MCP commit, rotate the active/previous role-access key
pair if required, then run `compose-up.sh`. It verifies the exact Handshake
SHA before Docker starts and atomically replaces the private host files.
3. Install the matching MCP commit and rotate the active/previous role-access
key pair if required. From that exact checkout, run
`sudo infra/scripts/install-clockchain-mcp-deploy-assets.sh`. The installer
first refreshes the out-of-checkout `compose-up.sh` and systemd unit, then
restarts the service. The refreshed wrapper verifies the exact Handshake SHA
before Docker starts and atomically replaces the private host files. Never
restart the service directly after changing the checkout: systemd deliberately
executes `/opt/clockchain-mcp/compose-up.sh`, not the copy inside the repo.
4. Deploy Research only after the production MCP manifest reports the same
helper digest and host-root ring that Research pins.

Expand Down
2 changes: 1 addition & 1 deletion infra/clockchain-mcp/compose-up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ validate_mcp_runtime_config() {

validate_v2_server_config() {
local release_filter access_filter active_kid previous_kid
release_filter='type == "object" and (keys | sort) == ["allowedAssetPrefix","hostRoots","manifestDigest","sourceCommit","version"] and .version == "2.1.0" and (.sourceCommit | test("^[0-9a-f]{40}$")) and (.manifestDigest | test("^[0-9a-f]{64}$")) and .allowedAssetPrefix == "https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.0/" and (.hostRoots | type == "array" and length >= 1 and length <= 2 and all(.[]; type == "object" and (keys | sort) == ["fingerprint","kid"] and (.kid | test("^[a-z0-9][a-z0-9-]{0,63}$")) and (.fingerprint | test("^[0-9a-f]{64}$"))))'
release_filter='type == "object" and (keys | sort) == ["allowedAssetPrefix","hostRoots","manifestDigest","sourceCommit","version"] and .version == "2.1.2" and (.sourceCommit | test("^[0-9a-f]{40}$")) and (.manifestDigest | test("^[0-9a-f]{64}$")) and .allowedAssetPrefix == "https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.2/" and (.hostRoots | type == "array" and length >= 1 and length <= 2 and all(.[]; type == "object" and (keys | sort) == ["fingerprint","kid"] and (.kid | test("^[a-z0-9][a-z0-9-]{0,63}$")) and (.fingerprint | test("^[0-9a-f]{64}$"))))'
access_filter='type == "object" and (keys | sort) == ["kid","secretBase64"] and (.kid | test("^[a-z0-9][a-z0-9-]{0,63}$")) and (.secretBase64 | @base64d | length >= 32)'

if ! jq -e "$release_filter" >/dev/null 2>&1 <<<"$AGENT_HANDSHAKE_RELEASE_PIN"; then
Expand Down
22 changes: 18 additions & 4 deletions infra/test/deploy-assets.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const expectedEnv = {
CLOCKCHAIN_API_KEY: "api-key-line-1\napi-key-line-2\n",
MCP_AUTH_TOKENS: "token-a,token-b\n",
MCP_TOKEN_SIGNING_SECRET: "signing-secret\nwith-newline\n",
AGENT_HANDSHAKE_RELEASE_PIN: '{"version":"2.1.0","sourceCommit":"0123456789abcdef0123456789abcdef01234567","manifestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","allowedAssetPrefix":"https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.0/","hostRoots":[{"kid":"root-2026-08","fingerprint":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}\n',
AGENT_HANDSHAKE_RELEASE_PIN: '{"version":"2.1.2","sourceCommit":"0123456789abcdef0123456789abcdef01234567","manifestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","allowedAssetPrefix":"https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.2/","hostRoots":[{"kid":"root-2026-08","fingerprint":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}\n',
AGENT_HANDSHAKE_ROLE_ACCESS_ACTIVE: '{"kid":"role-active","secretBase64":"YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE="}\n',
AGENT_HANDSHAKE_ROLE_ACCESS_PREVIOUS: '{"kid":"role-previous","secretBase64":"YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI="}\n',
};
Expand Down Expand Up @@ -187,7 +187,7 @@ case "$name" in
/clockchain/mcp/CLOCKCHAIN_API_KEY) value=$'api-key-line-1\\napi-key-line-2\\n' ;;
/clockchain/mcp/MCP_AUTH_TOKENS) value=$'token-a,token-b\\n' ;;
/clockchain/mcp/MCP_TOKEN_SIGNING_SECRET) value=$'signing-secret\\nwith-newline\\n' ;;
/clockchain/mcp/AGENT_HANDSHAKE_RELEASE_PIN) value=$'{"version":"2.1.0","sourceCommit":"0123456789abcdef0123456789abcdef01234567","manifestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","allowedAssetPrefix":"https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.0/","hostRoots":[{"kid":"root-2026-08","fingerprint":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}\\n' ;;
/clockchain/mcp/AGENT_HANDSHAKE_RELEASE_PIN) value=$'{"version":"2.1.2","sourceCommit":"0123456789abcdef0123456789abcdef01234567","manifestDigest":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","allowedAssetPrefix":"https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.2/","hostRoots":[{"kid":"root-2026-08","fingerprint":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]}\\n' ;;
/clockchain/mcp/AGENT_HANDSHAKE_ROLE_ACCESS_ACTIVE) value=$'{"kid":"role-active","secretBase64":"YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE="}\\n' ;;
/clockchain/mcp/AGENT_HANDSHAKE_ROLE_ACCESS_PREVIOUS) value=$'{"kid":"role-previous","secretBase64":"YmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI="}\\n' ;;
/clockchain/host/FUNDING_WALLET_JSON) value=$'{"wallet":"line-1\\\\nline-2"}\\n' ;;
Expand Down Expand Up @@ -297,10 +297,10 @@ async function resolvedComposeConfig() {
MCP_AUTH_TOKENS: "dummy-token",
MCP_TOKEN_SIGNING_SECRET: "dummy-signing",
AGENT_HANDSHAKE_RELEASE_PIN: JSON.stringify({
version: "2.1.0",
version: "2.1.2",
sourceCommit: expectedHandshakeSha,
manifestDigest: "a".repeat(64),
allowedAssetPrefix: "https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.0/",
allowedAssetPrefix: "https://github.com/thetangstr/clockchain-handshake-v2/releases/download/v2.1.2/",
hostRoots: [{ kid: "root-2026-08", fingerprint: "b".repeat(64) }],
}),
AGENT_HANDSHAKE_ROLE_ACCESS_ACTIVE: "dummy-role-active",
Expand Down Expand Up @@ -753,6 +753,20 @@ test("installer enables and restarts the systemd unit", async () => {
assert.match(install, /systemctl restart clockchain-mcp\.service/);
});

test("release runbook reinstalls deploy assets before every MCP restart", async () => {
const runbook = await readFile(path.join(deployDir, "RUNBOOK.md"), "utf8");
assert.match(
runbook,
/infra\/scripts\/install-clockchain-mcp-deploy-assets\.sh/,
"deploys must refresh the out-of-checkout systemd wrapper before restart",
);
assert.doesNotMatch(
runbook,
/then run `compose-up\.sh`/,
"the copied wrapper must not be invoked without first reinstalling it",
);
});

test("provisioning IAM policy is limited to MCP and host SSM prefixes", async () => {
const provision = await readFile(path.join(repoRoot, "infra", "scripts", "provision-clockchain-mcp-host.sh"), "utf8");
assert.match(provision, /parameter\/clockchain\/mcp\/\*/);
Expand Down
Loading
Loading