feat: blockchain-inspired World Ledger for agent activity tracking#92
Conversation
Append-only event ledger with hash chain and world signatures: - WorldLedger class: genesis block, hash chain (SHA-256), Ed25519-signed entries - Events: world.genesis, world.join, world.leave, world.evict, world.action - State derived from event replay (getAgentSummaries) - Persisted as JSON Lines (.jsonl), survives server restarts - Chain integrity verification (verify()) - HTTP endpoints: /world/ledger (with filtering), /world/agents - Auto-integrated into world-server.ts onMessage handlers - 13 new tests covering chain integrity, persistence, tampering, filtering Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Jing-yilin
left a comment
There was a problem hiding this comment.
Codex Review
[High] Corrupted/truncated ledger tails are silently discarded and verify() still reports success
WorldLedger.load() swallows JSON parse failures and just keeps the valid prefix in memory. That means a partial append or truncated tail line is treated as if it never existed, and verify() only checks the surviving prefix. I reproduced this by appending an invalid JSON fragment to a valid ledger file: the reloaded ledger dropped the broken line, length shrank to 2, and verify() still returned { ok: true }. For an append-only integrity feature, silently accepting data loss like this defeats the tamper/crash detection story.
Refs: packages/agent-world-sdk/src/world-ledger.ts:41, packages/agent-world-sdk/src/world-ledger.ts:166
[Medium] /world/agents reports stale agents as online after a server restart
getAgentSummaries() derives online purely from the durable event log, and /world/agents returns that value directly. After a restart, agentLastSeen is empty, but any agent whose last persisted event was world.join is still exposed as online: true until another leave/evict entry is appended. I reproduced this by starting createWorldServer, appending a join, stopping it, starting a new server against the same dataDir, and hitting /world/agents; the second process reported the old agent as online even though no session had rejoined.
Refs: packages/agent-world-sdk/src/world-ledger.ts:121, packages/agent-world-sdk/src/world-server.ts:204
[Low] The new HTTP API never applies the advertised until filter
LedgerQueryOpts supports an upper time bound, but /world/ledger only parses agent_id, event, since, and limit. The SDK method can filter by until; the HTTP endpoint cannot, so the public API doesn’t match the documented/queryable surface promised by this PR.
Refs: packages/agent-world-sdk/src/types.ts:181, packages/agent-world-sdk/src/world-server.ts:188
Verification run:
npm --prefix packages/agent-world-sdk run buildnpm run buildnode --test test/*.test.mjs
- [High] verify() now detects corrupted/dropped lines via corruptedLines counter and seq gap detection — truncated ledger no longer silently passes verification - [Medium] getAgentSummaries(liveAgentIds?) accepts optional live session set; /world/agents passes agentLastSeen keys so online status reflects reality after restart - [Low] /world/ledger endpoint now parses the 'until' query parameter - 2 new tests: corrupted line detection, liveAgentIds online status Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ull semver
DAP plugin (src/identity.ts, src/peer-server.ts) was using the raw
package.json version (e.g. '0.4.3') as PROTOCOL_VERSION in HTTP
signatures and domain separators. This causes signature verification
failures between nodes on different patch versions (0.4.3 vs 0.4.4).
Now extracts major.minor ('0.4') matching the SDK's approach, so
signatures remain compatible across patch releases.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Summary
Append-only event ledger with hash chain and world signatures, inspired by blockchain design.
Core Design
world.genesisentry on world startupprevHash(SHA-256 of previous entry)Changes
New:
WorldLedgerclass (packages/agent-world-sdk/src/world-ledger.ts).jsonlgetAgentSummaries()— derive current state from event replayverify()— validate entire chain integrityIntegration into
world-server.tsGET /world/ledger— query ledger entries with filtersGET /world/agents— agent summaries derived from ledgerWorldServerreturn valueTypes (
types.ts)LedgerEntry,LedgerEvent,AgentSummary,LedgerQueryOptsTests (
test/world-ledger.test.mjs)Test Results
144/144 tests pass (131 existing + 13 new)