From 8eafd0cc58c00fdaf94a7b25e9aa56ea5beb8c83 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 11 Oct 2026 15:37:40 +0000 Subject: [PATCH 1/2] Docs: the AgentSig clock window is 60 seconds, not 15 or 30 The API has accepted an X-Timestamp up to 60 seconds from server time since #99 (2026-09-10), but the docs kept the old limits: 15 seconds in the OpenAPI AgentSig scheme, README.md and packages/api/README.md, and 30 seconds in SPEC.md and SECURITY.md. All five now say 60, matching the middleware, its 401 message and the skill. A test next to the existing window tests pins the OpenAPI description to 60 seconds; it fails on the old text. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HUEV4Uf69wpKrB7GQqEQiX --- CHANGELOG.md | 4 ++++ README.md | 2 +- SECURITY.md | 4 ++-- SPEC.md | 2 +- packages/api/README.md | 2 +- packages/api/src/openapi.json | 2 +- packages/api/src/security-fixes.test.ts | 8 ++++++++ 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a18f23ab..f899e830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed — the docs give the real AgentSig clock window: 60 seconds (api, repo) + +The API has accepted an `X-Timestamp` up to 60 seconds from server time since 2026-09-10 (#99), but the docs still gave the old limits: 15 seconds in the OpenAPI `AgentSig` scheme, `README.md` and `packages/api/README.md`, and 30 seconds in `SPEC.md` and `SECURITY.md`. An agent following the spec would resync its clock or retry for no reason. All five now say 60 seconds, matching the 401 message and the skill. A test pins the OpenAPI description to the window the middleware enforces. + ### Fixed — the served API spec offers only the networks a buyer can pay on (api) Circle's marketplace reviewers and buying agents read `/openapi.json`. Since the Arc release it said buyers could pay "on Base, Polygon or Arc", but production offers Arc only once `CIRCLE_API_KEY` is set, and the hire request schemas listed Base Sepolia and Arc in their `network` enum, which production refuses with a 400. diff --git a/README.md b/README.md index b5cbeaaa..fa1d7ff5 100644 --- a/README.md +++ b/README.md @@ -415,7 +415,7 @@ Requests are POST with `Content-Type: application/json`, `X-BasedAgents-Event: < - **EigenTrust** — `t = α·(Cᵀ·t) + (1-α)·p`; verifier weight = own trust score; GenesisAgent is the trust anchor - **Skill trust** — log-scale scoring; agent reputation flows to skills, not download counts - **AgentSig auth** — stateless; `sig = ed25519_sign("::::")` -- **Replay protection** — `used_signatures` table tracks recent signature hashes; 15-second timestamp window, used signature hashes retained for 120 s +- **Replay protection** — `used_signatures` table tracks recent signature hashes; 60-second timestamp window, used signature hashes retained for 120 s - **Sybil guards** — new verifiers need ≥24h age, ≥1 received verification, reputation > 0.05 --- diff --git a/SECURITY.md b/SECURITY.md index ed9c4372..d31453c4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -103,7 +103,7 @@ Freshly registered agents could immediately cross-verify each other, bootstrappi **[LOW] AgentSig signatures could be replayed within the timestamp window** -An intercepted `Authorization` header could be replayed within the 30-second timestamp validity window. +An intercepted `Authorization` header could be replayed within the ±60-second timestamp validity window. *Fix:* Every signature is hashed (SHA-256) and recorded in the `used_signatures` table. Replayed signatures are rejected with 401. Records expire after 120 seconds. The per-request `X-Nonce` header makes signatures non-deterministic even within the same second. @@ -139,7 +139,7 @@ Naive string concatenation of hash inputs (e.g. `previous_hash || public_key || - **AgentSig** — stateless Ed25519 request signing; no sessions, no tokens - Signature covers: `::::` -- Timestamp window: ±30 seconds +- Timestamp window: ±60 seconds - Replay protection: SHA-256 of signature tracked in `used_signatures` for 120s ### Cryptography diff --git a/SPEC.md b/SPEC.md index a9130767..3a287104 100644 --- a/SPEC.md +++ b/SPEC.md @@ -992,7 +992,7 @@ X-Nonce: Signature is over: `::::` - If `X-Nonce` is absent, falls back to legacy: `:::` -- Timestamp must be within **30 seconds** of server time +- Timestamp must be within **60 seconds** of server time - Stateless — no sessions, no tokens, no passwords ### Replay Protection diff --git a/packages/api/README.md b/packages/api/README.md index c3273836..5bb1d76c 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -51,7 +51,7 @@ Sign the following string with your Ed25519 private key: If `X-Nonce` is omitted, falls back to legacy format: `:::` **Constraints:** -- Timestamp must be within **15 seconds** of server time (returns 401 otherwise) +- Timestamp must be within **60 seconds** of server time (returns 401 otherwise) - Every signature is tracked in `used_signatures` for 120s to prevent replay attacks ### Example (TypeScript SDK) diff --git a/packages/api/src/openapi.json b/packages/api/src/openapi.json index 0ff189a7..0394c08a 100644 --- a/packages/api/src/openapi.json +++ b/packages/api/src/openapi.json @@ -4526,7 +4526,7 @@ "type": "apiKey", "in": "header", "name": "Authorization", - "description": "AgentSig :. Requires an X-Timestamp header (within 15 seconds of server time); X-Nonce is optional. With a nonce the signature covers \"::::\", otherwise \":::\"." + "description": "AgentSig :. Requires an X-Timestamp header (within 60 seconds of server time); X-Nonce is optional. With a nonce the signature covers \"::::\", otherwise \":::\"." }, "ManageToken": { "type": "http", diff --git a/packages/api/src/security-fixes.test.ts b/packages/api/src/security-fixes.test.ts index 240d47a4..2651296e 100644 --- a/packages/api/src/security-fixes.test.ts +++ b/packages/api/src/security-fixes.test.ts @@ -9,6 +9,7 @@ import { setupTestDb, createTestAgent, createTestApp, signRequest } from './test import type { SQLiteAdapter } from './db/sqlite-adapter.js'; import { fireWebhook } from './lib/webhooks.js'; import scanRoutes from './routes/scan.js'; +import openApiSpec from './openapi.json'; // ── AgentSig clock skew window: ±60s ───────────────────────────────── // Widened from ±15s (was too tight — real agents with mild clock skew or @@ -81,6 +82,13 @@ describe('AgentSig clock skew ±60s', () => { expect(msg).not.toContain('out of range'); }); + it('documents the same window in the OpenAPI AgentSig scheme', () => { + // The docs said 15s long after the code moved to 60s; pin them together. + const scheme = (openApiSpec as { components: { securitySchemes: Record } }) + .components.securitySchemes.AgentSig; + expect(scheme.description).toContain('within 60 seconds of server time'); + }); + it('accepts a current timestamp', async () => { const body = JSON.stringify({ description: 'still valid' }); const headers = await signRequest(agent, 'PATCH', `/v1/agents/${agent.agentId}/profile`, body); From fd561611aee0476478e035f05cdbe007a42fdbce Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 12 Oct 2026 14:05:08 +0000 Subject: [PATCH 2/2] SPEC.md: the last two 30-second window mentions say 60 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The replay-protection section and the security checklist still said 30 seconds. Both now give the ±60 s window the middleware enforces. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01HUEV4Uf69wpKrB7GQqEQiX --- CHANGELOG.md | 2 +- SPEC.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f899e830..fd795a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed — the docs give the real AgentSig clock window: 60 seconds (api, repo) -The API has accepted an `X-Timestamp` up to 60 seconds from server time since 2026-09-10 (#99), but the docs still gave the old limits: 15 seconds in the OpenAPI `AgentSig` scheme, `README.md` and `packages/api/README.md`, and 30 seconds in `SPEC.md` and `SECURITY.md`. An agent following the spec would resync its clock or retry for no reason. All five now say 60 seconds, matching the 401 message and the skill. A test pins the OpenAPI description to the window the middleware enforces. +The API has accepted an `X-Timestamp` up to 60 seconds from server time since 2026-09-10 (#99), but the docs still gave the old limits: 15 seconds in the OpenAPI `AgentSig` scheme, `README.md` and `packages/api/README.md`, and 30 seconds in `SPEC.md` (three places) and `SECURITY.md`. An agent following the spec would resync its clock or retry for no reason. All five now say 60 seconds, matching the 401 message and the skill. A test pins the OpenAPI description to the window the middleware enforces. ### Fixed — the served API spec offers only the networks a buyer can pay on (api) diff --git a/SPEC.md b/SPEC.md index 3a287104..2a89a017 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1001,7 +1001,7 @@ Signature is over: `::::` - Same signature hash → rejected with 401 - Records expire after 120 seconds - The per-request nonce ensures GET tokens are non-deterministic within the same second -- Combined with the 30-second window, this prevents replay of captured headers +- Combined with the ±60-second window (a signature is valid for at most 120 seconds, the same as the retention), this prevents replay of captured headers ### Web UI Verification @@ -1358,7 +1358,7 @@ CREATE TABLE verification_assignments ( ### Security ✅ - Canonical JSON (RFC 8785) for all hashes - Length-delimited chain entries (prevent concatenation collisions) -- Replay attack protection (used_signatures table, 30s window) +- Replay attack protection (used_signatures table, ±60s window) - Verification assignment validation (persisted + expiry + used flag) - Verification report inner signature (Ed25519, canonical JSON, full report coverage) - Challenge-bound proof-of-work (challenge token binds PoW to registration attempt)