Skip to content

feat: blockchain-inspired World Ledger for agent activity tracking#92

Open
Jing-yilin wants to merge 2 commits intofeat/domain-separated-signingfrom
feat/world-ledger
Open

feat: blockchain-inspired World Ledger for agent activity tracking#92
Jing-yilin wants to merge 2 commits intofeat/domain-separated-signingfrom
feat/world-ledger

Conversation

@Jing-yilin
Copy link
Contributor

Summary

Append-only event ledger with hash chain and world signatures, inspired by blockchain design.

Core Design

Blockchain Concept World Ledger Implementation
Genesis block world.genesis entry on world startup
Transaction Each agent event: join, action, leave, evict
Block hash chain Each entry contains prevHash (SHA-256 of previous entry)
Signed transaction Entries signed by world's Ed25519 identity
State = f(events) Agent summaries derived by replaying event log
Immutable ledger JSON Lines file, append-only

Changes

New: WorldLedger class (packages/agent-world-sdk/src/world-ledger.ts)

  • Append-only event log persisted as .jsonl
  • Hash chain: each entry references previous entry's SHA-256
  • World signature on every entry (domain-separated Ed25519)
  • getAgentSummaries() — derive current state from event replay
  • verify() — validate entire chain integrity
  • Query with filtering (by agent, event type, time range, limit)

Integration into world-server.ts

  • Auto-records join/leave/action/evict in ledger
  • GET /world/ledger — query ledger entries with filters
  • GET /world/agents — agent summaries derived from ledger
  • Ledger exposed on WorldServer return value

Types (types.ts)

  • LedgerEntry, LedgerEvent, AgentSummary, LedgerQueryOpts

Tests (test/world-ledger.test.mjs)

  • 13 new tests: genesis, hash chain, persistence, tamper detection, filtering, agent summaries

Test Results

144/144 tests pass (131 existing + 13 new)

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>
Copy link
Contributor Author

@Jing-yilin Jing-yilin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 build
  • npm run build
  • node --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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant