16 KiB
Index0 Phase 0 Implementation Spec (2026-03-05)
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.