The README had zero mentions of the TypeScript implementation -- on the branch proposing to make it the main line, a reader (including PR #989's reviewers) would find only the Python product. A bridge section near the top now says what the rebuild is, its status (under independent review; do not put irreplaceable secrets in it yet), and where the migration guide, the vault/identity spec, and the cutover plan live. Deliberately a bridge, not a rewrite: everything below it still accurately describes the Python version, which is what install.sh installs today. Advertising the TS implementation as the product before its review lands would be docs describing an aspirational state -- the exact failure mode this branch has been stamping out. The 14 Python-era planning docs most likely to mislead (tui_v2/v3, index0/atlas, semantic) carry a one-line legacy banner so a reader can tell a historical record from a living document. The full TS-first rewrite -- README, landing page, docs triage, CI scoping, dependabot config -- is specced as a merge-day checklist under the cutover item in TODO.md, so main never advertises a stale product in either direction. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Tkj4rzKBA3uhfgk7kqjsb8
16 KiB
Index0 Phase 0 Implementation Spec (2026-03-05)
Legacy (Python implementation). This document describes the Python version, which retires to
legacy/after the TypeScript cutover (PR #989). The TypeScript interactive mode follows the classic v1 menus; TUI v2/v3, index0/atlas, and semantic search were not ported. Kept as historical record.
Status: In Progress
Branch target: beta
Depends on: docs/index0_atlas_execution_plan_2026-03-05.md
Companion next-layer plan: docs/atlas_search_graph_integration_plan_2026-03-05.md
1) Goal
Lock the Phase 0 implementation contract for index0 so coding can begin without schema drift.
Implementation update (2026-03-05):
- namespace normalization is implemented
- deterministic merge helpers are implemented
- manifest model round-trip support is implemented
- CRUD/link event emission is implemented
- daily checkpoint rebuild and bounded checkpoint retention are implemented
- manifest head/checkpoint publication from live vault state is implemented
- canonical synced
children_of,counts_by_kind, andrecent_activityviews are implemented - atlas read service and first v3 wayfinder consumer are implemented
- first actionable v3 wayfinder navigation flows are implemented
Remaining follow-on work for this spec:
- deeper search/navigation handoff from atlas wayfinder data
- agent-facing atlas workflows over canonical views/events
- any later event-pruning compaction policy
Delivered foundation summary:
index0is no longer speculative inbeta- canonical storage, merge, event emission, checkpoints, manifest metadata, and first synced views are implemented
- atlas reads are exposed via
AtlasService - v3 already consumes atlas data in both passive and actionable ways
This document defines:
- canonical payload schema
- manifest extension fields
- normalization and hashing rules
- deterministic merge rules
- deterministic compaction defaults
- first implementation slice
- first test matrix
2) Canonical Payload Contract
index0 lives inside the same encrypted vault payload in a reserved namespace:
{
"schema_version": 3,
"entries": {},
"_sync_meta": {},
"_system": {
"index0": {
"schema_version": 1,
"events": {},
"checkpoints": {},
"canonical_views": {},
"view_manifest": {},
"heads": {},
"stats": {}
}
}
}
Normalization requirements:
- missing
_systemmust be treated as{} - missing
_system.index0must be initialized during load or merge - all
events,checkpoints,canonical_views,heads, andstatsmembers must be JSON objects - all keys must be ASCII strings
- the canonical JSON hash function must use
sort_keys=Trueand compact separators(",", ":")
3) Schema Definitions
3.1 index0_event
Storage key:
events[event_id]
Required fields:
{
"event_id": "e:sha256hex",
"event_type": "entry_created",
"subject_type": "entry",
"subject_id": "42",
"subject_kind": "document",
"scope_path": "seed/<root>/managed/<child>",
"actor_type": "user",
"actor_id": "<profile fingerprint or agent id>",
"writer_id": "writer:<scope-or-profile>",
"modified_ts": 1760000000,
"prev_hash": "",
"classification": "internal",
"partition": "standard",
"payload_ref": {
"entry_id": "42"
},
"links": [],
"tags": [],
"visibility": "private",
"integrity_hash": "sha256hex"
}
Optional fields:
{
"policy_ref": "<policy stamp or stable rule id>",
"source": "entry_management",
"source_event_id": "<nostr event id or external ref>",
"summary": "<short safe summary>"
}
Enum constraints:
event_type:entry_createdentry_modifiedentry_deletedentry_archivedentry_restoredlink_addedlink_removedprofile_addedprofile_removedagent_addedagent_removedsync_snapshot_publishedsync_delta_appliednostr_message_indexeddocument_indexed
subject_type:entryprofileagentconversationdocumentsync
actor_type:useragentsystem
classification:publicinternalrestrictedsecret
partition:standardhigh_risk
visibility:publicprivatepolicy_scoped
Normalization rules:
event_id,subject_id,actor_id, andwriter_idare stored as stringsmodified_tsmust be an integer greater than zeroprev_hashdefaults to""linksmust use the same canonical link normalization rules already used for entriestagsmust be unique and lexicographically sortedsummarymust never contain secret materialintegrity_hashis computed from the canonical event body excluding theintegrity_hashfield itself
3.2 index0_checkpoint
Storage key:
checkpoints[checkpoint_id]
Required fields:
{
"checkpoint_id": "cp:day:2026-03-05:writer:<root>",
"window_type": "day",
"window_key": "2026-03-05",
"writer_id": "writer:<root>",
"window_start_ts": 1760000000,
"window_end_ts": 1760086399,
"event_count": 512,
"head_hash": "sha256hex",
"summary_hash": "sha256hex",
"rollup": {
"events_by_type": {},
"subjects_by_kind": {},
"subjects": []
},
"modified_ts": 1760086399
}
Normalization rules:
- initial implementation supports only
window_type = "day" checkpoint_idmust be deterministic fromwindow_type,window_key, andwriter_idsubjectsmust be a sorted unique list and capped deterministicallysummary_hashis computed from the canonical checkpoint body excludingsummary_hash
3.3 canonical_view
Storage key:
canonical_views[view_id]
Required fields:
{
"view_id": "children_of:seed/<root>",
"view_type": "children_of",
"scope_path": "seed/<root>",
"source_checkpoint_ids": [],
"source_event_ids": [],
"data": {},
"modified_ts": 1760086400,
"view_hash": "sha256hex"
}
Normalization rules:
- only compact low-cardinality views are eligible for canonical sync in Phase 1
datamust be deterministic and sorted by stable keysview_hashis computed from the canonical view body excludingview_hash
3.4 view_manifest
Purpose:
- declare canonical synced views
- version view builders
- make local rebuild policy explicit
Schema:
{
"version": 1,
"canonical_view_types": [
"children_of",
"counts_by_kind",
"recent_activity"
],
"local_only_view_types": [
"hot_nodes",
"conversation_index",
"semantic_neighbors"
],
"builder_versions": {
"children_of": 1,
"counts_by_kind": 1,
"recent_activity": 1
}
}
3.5 heads
Purpose:
Track current writer-stream head hashes.
Schema:
{
"writer:<root>": {
"event_id": "e:sha256hex",
"head_hash": "sha256hex",
"modified_ts": 1760086400
}
}
Rules:
- each
writer_idhas at most one head record - higher
modified_tswins - equal timestamp resolves by lexicographically larger
head_hash
3.6 stats
Initial synced stats:
{
"event_count": 1280,
"checkpoint_count": 14,
"writer_count": 3,
"last_compaction_ts": 1760086400,
"last_validation_ts": 1760086400
}
Stats are advisory and deterministically derived. They are never the source of truth.
4) Manifest Extension Contract
The current manifest already carries snapshot metadata. Extend it with optional index0 fields:
{
"ver": 1,
"algo": "gzip",
"chunks": [],
"delta_since": 1760086400,
"nonce": "base64...",
"index0": {
"schema_version": 1,
"checkpoint_ids": [
"cp:day:2026-03-05:writer:<root>"
],
"checkpoint_hashes": {
"cp:day:2026-03-05:writer:<root>": "sha256hex"
},
"stream_heads": {
"writer:<root>": "sha256hex"
}
}
}
Manifest rules:
- manifest
index0section is optional for backward compatibility - restore must accept manifests with no
index0 - manifest
index0metadata is validation material, not source-of-truth state - if present, it must be recomputable from the decrypted payload after replay
5) Hashing And ID Rules
5.1 Canonical helper
Use the same canonical JSON discipline already used by the sync layer:
json.dumps(value, sort_keys=True, separators=(",", ":"))
5.2 Event body hash
Pseudo:
def canonical_event_body(event):
body = dict(event)
body.pop("integrity_hash", None)
return body
def compute_event_hash(event):
return sha256(canonical(canonical_event_body(event)))
5.3 Event ID
For Phase 1, event IDs should be content-addressed and deterministic:
event_id = "e:" + compute_event_hash(event)
This intentionally makes duplicate event publication idempotent.
5.4 Head hash
Pseudo:
def compute_head_hash(event):
marker = {
"event_id": event["event_id"],
"integrity_hash": event["integrity_hash"],
"prev_hash": event.get("prev_hash", ""),
"writer_id": event["writer_id"],
}
return sha256(canonical(marker))
5.5 Checkpoint ID
Pseudo:
checkpoint_id = f"cp:day:{window_key}:{writer_id}"
5.6 Checkpoint hash
Pseudo:
def compute_checkpoint_hash(checkpoint):
body = dict(checkpoint)
body.pop("summary_hash", None)
return sha256(canonical(body))
6) Writer Model
Phase 1 writer rule:
writer_iddefaults to profile scope, not device instance- format:
- root profile:
writer:profile:<fingerprint> - managed scope:
writer:profile:<fingerprint>:managed:<subject> - agent scope later if needed:
writer:agent:<agent_id>
- root profile:
Rationale:
- profile-scoped writers are simpler and stable across devices
- this fits current
betamulti-device deterministic merge better than device-scoped streams - agent-specific streams can be added later without invalidating the contract
7) Deterministic Merge Rules
merge_index_payloads(...) remains the top-level orchestrator.
Add:
merge_system_index0(current_index0, incoming_index0) -> merged_index0
7.1 Event merge
Rules:
- merge by
event_id - if only one side contains an event, keep it
- if both sides contain the same
event_idand canonical bodies match, keep one - if both sides contain the same
event_idbut bodies differ, pick the event with lexicographically larger canonical body hash and record a validation warning path later
This last case should be treated as corruption-suspicious and covered by tests even if it should not occur in normal generation.
7.2 Head merge
Rules:
- merge by
writer_id - higher
modified_tswins - equal timestamp resolves by larger
head_hash
7.3 Checkpoint merge
Rules:
- merge by
checkpoint_id - if both sides contain the same
checkpoint_idwith different bodies, select lexicographically largersummary_hash - equal bodies remain idempotent
7.4 Canonical view merge
Rules:
- merge by
view_id - higher
modified_tswins - equal timestamp resolves by larger
view_hash - local-only views must never be written into canonical synced payload
7.5 Stats merge
Rules:
- stats are recomputed from merged payload where practical
- if not recomputed, use deterministic max/union semantics only
8) Merge Pseudocode
def merge_system_index0(current, incoming):
cur = normalize_index0(current)
inc = normalize_index0(incoming)
merged_events = merge_events(cur["events"], inc["events"])
merged_heads = merge_heads(cur["heads"], inc["heads"])
merged_checkpoints = merge_checkpoints(
cur["checkpoints"], inc["checkpoints"]
)
merged_views = merge_canonical_views(
cur["canonical_views"], inc["canonical_views"], cur["view_manifest"], inc["view_manifest"]
)
merged_view_manifest = merge_view_manifest(
cur["view_manifest"], inc["view_manifest"]
)
merged = {
"schema_version": max(cur["schema_version"], inc["schema_version"]),
"events": merged_events,
"checkpoints": merged_checkpoints,
"canonical_views": merged_views,
"view_manifest": merged_view_manifest,
"heads": merged_heads,
"stats": recompute_stats(
merged_events, merged_checkpoints, merged_heads
),
}
return compact_index0(merged)
9) Deterministic Compaction Defaults
Phase 1 compaction must stay intentionally simple.
Defaults:
- raw event retention per writer:
- keep last
512raw events
- keep last
- raw event retention by age:
- keep last
30days
- keep last
- checkpoint window:
- daily
- canonical view retention:
- keep only latest view per
view_id
- keep only latest view per
- checkpoint subject list cap:
128
Compaction order:
- group events by
writer_id - within each writer, sort by:
modified_tsevent_id
- materialize daily checkpoints for prunable windows
- prune raw events only after checkpoint exists
- recompute heads and stats
Guarantee:
If two devices compact the same normalized payload, they must produce the same payload.
10) Initial Builder Scope
The first code slice should not try to solve the full knowledge-base product.
Phase 1 implementation should include only:
_system.index0schema initialization and migration- event emission for:
- entry create
- entry modify
- entry delete
- entry archive
- entry restore
- link add
- link remove
- merge support
- head maintenance
- manifest
index0metadata round-trip
Deferred until later phases:
- communication-specific event emission
- local rebuild daemons
- UI-facing atlas panels
- semantic interplay
- policy-filtered atlas export
11) Failure Handling
Rules:
- malformed
index0objects must normalize to empty structures where safe - malformed individual events/checkpoints/views should be dropped during normalization if required fields are invalid
- manifest
index0mismatches should warn by default in the first implementation - hard-fail behavior can be added later behind policy/config
12) Test Matrix
12.1 Unit
normalize_index0fills defaults and drops malformed objectscompute_event_hashis stablecompute_head_hashis stablecompute_checkpoint_hashis stable- tag normalization is deterministic
- checkpoint ID generation is deterministic
12.2 Merge
- event merge is idempotent
- event merge is order independent
- conflicting same-
event_idbodies resolve deterministically - head merge prefers newer
modified_ts - equal timestamp head merge prefers larger
head_hash - view merge rejects local-only view types in canonical payload
12.3 Replay / vault boundary
- payload with
_system.index0survives encrypt/decrypt round-trip - repeated merge through vault boundary is idempotent
- stale payload replay cannot remove newer heads/checkpoints
12.4 Migration
- old payload without
_systemupgrades cleanly - old payload with malformed
_system.index0repairs cleanly - existing entry CRUD remains unchanged
12.5 Nostr manifest
- manifest serializes and parses optional
index0 - restore accepts legacy manifest with no
index0 - restore warning path triggers on checkpoint/head mismatch
12.6 Integration
- add entry emits
entry_created - modify entry emits
entry_modified - delete path emits
entry_deleted - archive/restore emit expected events
- link add/remove emit expected events
13) Implementation Order
Recommended coding order:
- add normalization + hashing helpers in a new
index0module - extend merge logic with
merge_system_index0 - extend manifest dataclass and serializer/parser
- add payload migration/init in vault load path
- emit events from entry-management hooks
- add tests in lockstep
14) Definition Of Phase 0 Complete
Phase 0 is complete when:
- this schema contract is accepted
- manifest extension fields are accepted
- merge and compaction rules are accepted
- first implementation slice is accepted
- test inventory is accepted
At that point, implementation can move directly into Phase 1 without reopening architecture questions.