Documentation
¶
Overview ¶
snapshot_reads.go holds the producer-side reads for full-state snapshot production (P4-HUB-11, `devstrap hub compact`): the derived namespace map, surviving tombstones, per-device chain anchors, and the current local HLC. They mirror the wire shapes in internal/sync/snapshot.go but live in state to avoid an import cycle (sync imports state); internal/sync.BuildSnapshot maps them across.
Index ¶
- Variables
- func ContentHash(payloadJSON string) string
- func EffectiveKeyCustody(recorded devicekeys.Custody) devicekeys.Custody
- func EventSignaturePayload(event Event) []byte
- func EventSignaturePayloadV2(event Event) []byte
- func ValidateDBFile(ctx context.Context, path string) error
- type AgentRun
- type Conflict
- type Device
- type DraftSnapshot
- type DraftSnapshotRef
- type EnvProfile
- type Event
- type GitRepo
- type HeldKey
- type KeyGrantWait
- type NamespaceEntry
- type ProjectStatus
- type RenameOutcome
- type SecretBinding
- type SkippedEvent
- type SnapshotChainAnchor
- type SnapshotEntry
- type SnapshotEntryDraft
- type SnapshotEntryGit
- type SnapshotTombstone
- type Store
- func (s *Store) ActiveKeyEpochAge(ctx context.Context) (int64, time.Time, error)
- func (s *Store) AdvanceHubCursor(ctx context.Context, hubID string, hlc int64) error
- func (s *Store) AdvanceHubDeviceCursor(ctx context.Context, hubID, deviceID string, seq int64) error
- func (s *Store) AdvancePushSeqCursor(ctx context.Context, hubID string, seq int64) error
- func (s *Store) AgentRunByID(ctx context.Context, id string) (AgentRun, error)
- func (s *Store) AllBlobRefs(ctx context.Context) ([]string, error)
- func (s *Store) ApprovedDeviceSigningKey(ctx context.Context, deviceID string) (string, bool, error)
- func (s *Store) ApprovedRecipients(ctx context.Context) ([]string, error)
- func (s *Store) Backup(ctx context.Context, outputPath string) error
- func (s *Store) BlobRefCount(ctx context.Context) (map[string]int, error)
- func (s *Store) ChainAnchorsForFloors(ctx context.Context, floors map[string]int64) ([]SnapshotChainAnchor, error)
- func (s *Store) ClearAllBindingRotation(ctx context.Context) (int, error)
- func (s *Store) ClearPendingHubDelete(ctx context.Context, ref string) error
- func (s *Store) ClearRotationForProject(ctx context.Context, namespaceID string) (int, error)
- func (s *Store) Close() error
- func (s *Store) ConflictByID(ctx context.Context, id string) (Conflict, error)
- func (s *Store) CountAgentRunsByStatus(ctx context.Context, status string) (int, error)
- func (s *Store) CountOpenConflicts(ctx context.Context) (int, error)
- func (s *Store) CountSecretBindingsNeedingRotation(ctx context.Context) (int, error)
- func (s *Store) CountTombstonesBelowHLC(ctx context.Context, beforeHLC int64) (int, error)
- func (s *Store) CurrentDevice(ctx context.Context) (Device, error)
- func (s *Store) CurrentHLC(ctx context.Context) (int64, error)
- func (s *Store) CurrentKeyEpoch(ctx context.Context) (int64, error)
- func (s *Store) Down() error
- func (s *Store) DraftBlobRefs(ctx context.Context) ([]string, error)
- func (s *Store) DraftProjectLimits(ctx context.Context, namespaceID string) (int64, int64, error)
- func (s *Store) DraftSnapshotsForBlobRef(ctx context.Context, ref string) ([]DraftSnapshotRef, error)
- func (s *Store) EnsureDevice(ctx context.Context, name string) (Device, error)
- func (s *Store) EnsureDraftProject(ctx context.Context, namespaceID string) error
- func (s *Store) EnsureRemoteDevice(ctx context.Context, deviceID string) error
- func (s *Store) EnsureWorkspace(ctx context.Context, name, rootPath string) error
- func (s *Store) EnsureWorkspaceWithID(ctx context.Context, workspaceID, name, rootPath string) error
- func (s *Store) EnvBlobRefs(ctx context.Context) ([]string, error)
- func (s *Store) EnvProfileForProject(ctx context.Context, namespaceID string) (EnvProfile, []SecretBinding, error)
- func (s *Store) EventByID(ctx context.Context, id string) (Event, error)
- func (s *Store) ForeignKeyCheck(ctx context.Context) (string, error)
- func (s *Store) GCTombstones(ctx context.Context, beforeHLC int64) (int, error)
- func (s *Store) GetLocalMeta(ctx context.Context, key string) (string, bool, error)
- func (s *Store) HasHubDeviceCursors(ctx context.Context, hubID string) (bool, error)
- func (s *Store) HeldKeyEpochs(ctx context.Context) ([]int64, error)
- func (s *Store) HeldKeys(ctx context.Context) ([]HeldKey, error)
- func (s *Store) HubCursor(ctx context.Context, hubID string) (int64, error)
- func (s *Store) HubDeviceCursors(ctx context.Context, hubID string) (map[string]int64, error)
- func (s *Store) InsertAgentRun(ctx context.Context, run AgentRun) (AgentRun, error)
- func (s *Store) InsertConflict(ctx context.Context, namespaceID, typ, detailsJSON string) error
- func (s *Store) InsertEvent(ctx context.Context, event Event) error
- func (s *Store) InsertLocalEvent(ctx context.Context, event Event) (Event, error)
- func (s *Store) InsertLocalEventTx(ctx context.Context, tx *Tx, event Event) (Event, error)
- func (s *Store) InsertWorktree(ctx context.Context, wt Worktree) (Worktree, error)
- func (s *Store) KeyCustody(ctx context.Context) (devicekeys.Custody, error)
- func (s *Store) LatestDraftSnapshot(ctx context.Context, namespaceID string) (*DraftSnapshot, error)
- func (s *Store) ListAgentRuns(ctx context.Context) ([]AgentRun, error)
- func (s *Store) ListDevices(ctx context.Context) ([]Device, error)
- func (s *Store) ListProjects(ctx context.Context) ([]ProjectStatus, error)
- func (s *Store) ListWorktrees(ctx context.Context) ([]Worktree, error)
- func (s *Store) LocalPendingEvents(ctx context.Context, afterHLC int64) ([]Event, error)
- func (s *Store) LocalPendingEventsBySeq(ctx context.Context, afterSeq int64) ([]Event, error)
- func (s *Store) MarkEncryptedBindingsNeedingRotation(ctx context.Context) (int, error)
- func (s *Store) MarkWorktreeRemoved(ctx context.Context, id string) error
- func (s *Store) Migrate() error
- func (s *Store) NoteMissingKeyGrant(ctx context.Context, epoch int64, kid string) (time.Time, error)
- func (s *Store) NoteSkippedEvent(ctx context.Context, ev Event, reason string) (time.Time, error)
- func (s *Store) OpenConflicts(ctx context.Context) ([]Conflict, error)
- func (s *Store) OpenConflictsByType(ctx context.Context, typ string) ([]Conflict, error)
- func (s *Store) OpenKeyGrantWaits(ctx context.Context) ([]KeyGrantWait, error)
- func (s *Store) OpenSkippedEvents(ctx context.Context) ([]SkippedEvent, error)
- func (s *Store) PendingEvents(ctx context.Context) ([]Event, error)
- func (s *Store) PendingHubDeletes(ctx context.Context) ([]string, error)
- func (s *Store) ProjectByID(ctx context.Context, id string) (ProjectStatus, error)
- func (s *Store) ProjectByPath(ctx context.Context, path string) (ProjectStatus, error)
- func (s *Store) PruneDraftSnapshots(ctx context.Context, keep int) (int, error)
- func (s *Store) PushSeqCursor(ctx context.Context, hubID string) (int64, error)
- func (s *Store) QueuePendingHubDelete(ctx context.Context, ref string) error
- func (s *Store) QuickCheck(ctx context.Context) (string, error)
- func (s *Store) RecordDraftSnapshot(ctx context.Context, namespaceID, blobRef string, byteSize, fileCount int64, ...) error
- func (s *Store) RecordKeyCustody(ctx context.Context, c devicekeys.Custody) error
- func (s *Store) RecordKeyEpoch(ctx context.Context, epoch int64, kid, origin string) error
- func (s *Store) RecordKeyGrant(ctx context.Context, epoch int64, kid string, recipient, sourceEventID string, ...) error
- func (s *Store) RenameDevice(ctx context.Context, deviceID, name string) error
- func (s *Store) ResolveConflict(ctx context.Context, id, resolutionJSON string) error
- func (s *Store) RetainedBlobRefs(ctx context.Context, keep int) ([]string, error)
- func (s *Store) RunningAgentRunsWithPID(ctx context.Context) ([]AgentRun, error)
- func (s *Store) SaveCapturedEnvProfile(ctx context.Context, namespaceID, name string, varNames []string, ...) (EnvProfile, error)
- func (s *Store) SaveProviderEnvProfile(ctx context.Context, namespaceID, name, provider string, ...) (EnvProfile, error)
- func (s *Store) SetDevicePublicKey(ctx context.Context, deviceID, publicKey string) error
- func (s *Store) SetDeviceSigningPublicKey(ctx context.Context, deviceID, publicKey string) error
- func (s *Store) SetDeviceTrustState(ctx context.Context, deviceID, trustState string) error
- func (s *Store) SetLocalMeta(ctx context.Context, key, value string) error
- func (s *Store) SetProjectForgeKind(ctx context.Context, namespaceID, kind string) error
- func (s *Store) SkeletonProjects(ctx context.Context) ([]ProjectStatus, error)
- func (s *Store) SnapshotEntries(ctx context.Context) ([]SnapshotEntry, error)
- func (s *Store) SnapshotTombstones(ctx context.Context) ([]SnapshotTombstone, error)
- func (s *Store) Summary(ctx context.Context) (Summary, error)
- func (s *Store) UpdateAgentRunResult(ctx context.Context, id, status, diffSummary, testSummary string) error
- func (s *Store) UpdateAgentRunStatus(ctx context.Context, id, status string) error
- func (s *Store) UpdateBlobRef(ctx context.Context, oldRef, newRef string) error
- func (s *Store) UpdateGitDefaultBranch(ctx context.Context, namespaceID, branch string) error
- func (s *Store) UpdateKeyKid(ctx context.Context, epoch int64, kid string) error
- func (s *Store) UpdateProjectLocalState(ctx context.Context, namespaceID, localPath, materialization, dirty string) error
- func (s *Store) UpsertDevice(ctx context.Context, device Device) error
- func (s *Store) UpsertProject(ctx context.Context, params UpsertProjectParams) (NamespaceEntry, error)
- func (s *Store) VerifyRemoteEvent(ctx context.Context, event Event) error
- func (s *Store) Version() (int64, error)
- func (s *Store) WithTx(ctx context.Context, fn func(*Tx) error) error
- func (s *Store) WorkspaceID(ctx context.Context) (string, error)
- func (s *Store) WorktreeByID(ctx context.Context, id string) (Worktree, error)
- type Summary
- type Tx
- func (tx *Tx) ClearSkippedEventTx(ctx context.Context, eventID string) error
- func (tx *Tx) EnsureRemoteDeviceTx(ctx context.Context, deviceID string) error
- func (tx *Tx) InsertConflict(ctx context.Context, namespaceID, typ, detailsJSON string) error
- func (tx *Tx) InsertEvent(ctx context.Context, event Event) (bool, error)
- func (tx *Tx) ProjectByPath(ctx context.Context, path string) (ProjectStatus, error)
- func (tx *Tx) ProjectByPathKey(ctx context.Context, pathKey string) (ProjectStatus, error)
- func (tx *Tx) ReceiveRemoteHLC(ctx context.Context, remoteHLC int64) error
- func (tx *Tx) RecordDraftSnapshotTx(ctx context.Context, namespaceID, blobRef string, byteSize, fileCount int64, ...) error
- func (tx *Tx) RecordKeyGrantTx(ctx context.Context, epoch int64, kid string, recipient string, event Event) error
- func (tx *Tx) RenameProject(ctx context.Context, oldPath, newPath string, event Event) (RenameOutcome, error)
- func (tx *Tx) ResolveConflict(ctx context.Context, id, resolutionJSON string) error
- func (tx *Tx) ResolveConflictByFingerprint(ctx context.Context, typ, detailsJSON, resolutionJSON string) error
- func (tx *Tx) ResolveOpenConflictsByEventID(ctx context.Context, typ, eventID, resolutionJSON string) error
- func (tx *Tx) TombstoneByPathKey(ctx context.Context, pathKey string, hlc int64) error
- func (tx *Tx) TombstoneHLC(ctx context.Context, path string) (int64, bool, error)
- func (tx *Tx) TombstoneProject(ctx context.Context, path string, hlc int64) error
- func (tx *Tx) UpsertChainAnchor(ctx context.Context, deviceID string, anchorSeq int64, contentHash string, ...) error
- func (tx *Tx) UpsertProject(ctx context.Context, params UpsertProjectParams) (NamespaceEntry, error)
- type UpsertProjectParams
- type Worktree
Constants ¶
This section is empty.
Variables ¶
var ErrDivergentEvent = errors.New("event id already exists with different immutable content")
var ErrEventHashChain = errors.New("event prev_event_hash chain break")
var ErrEventVerification = errors.New("event verification failed")
var ErrNotInitialized = errors.New("workspace is not initialized; run devstrap init")
var ErrWorkspaceIDMismatch = errors.New("workspace id mismatch")
ErrWorkspaceIDMismatch reports that an explicitly supplied workspace id (init --workspace-id) conflicts with the id this store was initialized under. There is no in-place rewrite path — the remedy is to remove the state home and re-run `devstrap init --join --workspace-id <id>` (P4-SEC-07).
Functions ¶
func ContentHash ¶
func EffectiveKeyCustody ¶
func EffectiveKeyCustody(recorded devicekeys.Custody) devicekeys.Custody
EffectiveKeyCustody applies the DEVSTRAP_NO_KEYCHAIN override to a recorded custody decision: when set, file custody is forced regardless of what was recorded, so headless/CI runs always use the file store (P6-XP-04). Otherwise the recorded decision (possibly unset) stands.
func EventSignaturePayload ¶
EventSignaturePayload is the LEGACY v1 signed payload (no DeviceID/Seq). It is retained only so verification can fall back to v1 for historical events; new signatures use EventSignaturePayloadV2.
func EventSignaturePayloadV2 ¶
EventSignaturePayloadV2 is the v2 signed payload: the v1 tuple plus DeviceID and Seq (P6-SYNC-04), keys in alphabetical order.
func ValidateDBFile ¶
ValidateDBFile runs quick_check + foreign_key_check on a standalone SQLite file (P6-DATA-04). It is the same validation Backup runs after VACUUM INTO, exported so `db restore` can prove an extracted state.db is intact before it is promoted into place.
Types ¶
type AgentRun ¶
type AgentRun struct {
ID string `json:"id"`
NamespaceID string `json:"namespace_id"`
WorktreeID string `json:"worktree_id,omitempty"`
Engine string `json:"engine"`
Task string `json:"task"`
PolicyID string `json:"policy_id,omitempty"`
Status string `json:"status"`
BaseRef string `json:"base_ref,omitempty"`
BaseSHA string `json:"base_sha,omitempty"`
Branch string `json:"branch,omitempty"`
LogPath string `json:"log_path,omitempty"`
DiffSummary string `json:"diff_summary,omitempty"`
TestSummary string `json:"test_summary,omitempty"`
RunnerPID int `json:"runner_pid,omitempty"`
}
type Device ¶
type Device struct {
ID string `json:"id"`
Name string `json:"name"`
OS string `json:"os"`
Arch string `json:"arch"`
Hostname string `json:"hostname,omitempty"`
PublicKey string `json:"public_key,omitempty"`
SigningPublicKey string `json:"signing_public_key,omitempty"`
TrustState string `json:"trust_state"`
}
type DraftSnapshot ¶
type DraftSnapshot struct {
ID string
NamespaceID string
BlobRef string
ByteSize int64
FileCount int64
SourceEventHLC int64
SourceEventDeviceID string
SourceEventID string
}
DraftSnapshot records a content-addressed age_blob bundle for a non-git project (DRAFT-02).
type DraftSnapshotRef ¶
DraftSnapshotRef is the metadata needed to re-emit a superseding draft.snapshot.created event when a draft blob is rewrapped (P5-SEC-01).
type EnvProfile ¶
type GitRepo ¶
type GitRepo struct {
NamespaceID string `json:"namespace_id"`
RemoteURL string `json:"remote_url"`
RemoteKey string `json:"remote_key"`
DefaultBranch string `json:"default_branch"`
CloneFilter string `json:"clone_filter,omitempty"`
LFSPolicy string `json:"lfs_policy"`
ForgeKind string `json:"forge_kind,omitempty"`
}
type HeldKey ¶
HeldKey is one WCK this device holds metadata for: an (epoch, kid) pair plus how the key was obtained (P6-SEC-02).
type KeyGrantWait ¶
KeyGrantWait is one still-open grace-window wait for a workspace key this device has seen ciphertext for but holds no key to (P6-SEC-03). KID is ” when the whole epoch is missing.
type NamespaceEntry ¶
type NamespaceEntry struct {
ID string `json:"id"`
Path string `json:"path"`
PathKey string `json:"path_key"`
Type string `json:"type"`
DisplayName string `json:"display_name,omitempty"`
MaterializationPolicy string `json:"materialization_policy"`
Status string `json:"status"`
SourceEventHLC int64 `json:"source_event_hlc,omitempty"`
SourceEventDeviceID string `json:"source_event_device_id,omitempty"`
SourceEventID string `json:"source_event_id,omitempty"`
}
type ProjectStatus ¶
type ProjectStatus struct {
NamespaceEntry
RemoteURL string `json:"remote_url,omitempty"`
RemoteKey string `json:"remote_key,omitempty"`
DefaultBranch string `json:"default_branch,omitempty"`
LFSPolicy string `json:"lfs_policy,omitempty"`
ForgeKind string `json:"forge_kind,omitempty"`
LocalPath string `json:"local_path,omitempty"`
MaterializationState string `json:"materialization_state,omitempty"`
DirtyState string `json:"dirty_state,omitempty"`
}
type RenameOutcome ¶
type RenameOutcome int
RenameOutcome reports how a project.renamed event was applied.
const ( // RenameApplied means the namespace entry was moved to the new path. RenameApplied RenameOutcome = iota // RenameSourceMissing means no active entry existed at the old path. RenameSourceMissing // RenameTargetConflict means the new path is already an active, distinct entry. RenameTargetConflict // RenameStale means the new path holds a newer tombstone (lost-delete guard). RenameStale )
type SecretBinding ¶
type SecretBinding struct {
ID string `json:"id"`
EnvProfileID string `json:"env_profile_id"`
VarName string `json:"var_name"`
ProviderRef string `json:"provider_ref,omitempty"`
EncryptedValueRef string `json:"encrypted_value_ref,omitempty"`
Required bool `json:"required"`
NeedsRotation bool `json:"needs_rotation,omitempty"`
}
type SkippedEvent ¶
type SkippedEvent struct {
EventID string
DeviceID string
Seq int64
HLC int64
Reason string
FirstSeenAt time.Time
}
SkippedEvent is one durable record of an event EncryptedHub.Pull dropped from the batch (P6-SYNC-02): under the per-device Seq cursor the drop holds the origin device's cursor at a seq gap, and this row is the visibility for that retry wedge. Reasons: "unknown-envelope-version" (recoverable by upgrading devstrap; first_seen_at is its grace clock), "retired-enc-v1" (re-found the workspace), "plaintext-anti-downgrade" (the hub is serving plaintext where ciphertext is required).
type SnapshotChainAnchor ¶
SnapshotChainAnchor is one origin device's hash-chain anchor for the snapshot: the content hash of the last event the snapshot covers for that device (at seq = floor-1).
type SnapshotEntry ¶
type SnapshotEntry struct {
Path string
PathKey string
Type string
DisplayName string
MaterializationPolicy string
Status string
SourceEventHLC int64
SourceEventDeviceID string
SourceEventID string
Git *SnapshotEntryGit
Draft *SnapshotEntryDraft
}
SnapshotEntry is one active namespace-map row with its source-event coordinates plus optional git/draft sub-objects, read for snapshot production.
type SnapshotEntryDraft ¶
type SnapshotEntryDraft struct {
BlobRef string
ByteSize int64
FileCount int64
SourceEventHLC int64
SourceEventDeviceID string
SourceEventID string
}
SnapshotEntryDraft is the latest draft-bundle pointer for a draft project.
type SnapshotEntryGit ¶
type SnapshotEntryGit struct {
RemoteURL string
RemoteKey string
DefaultBranch string
LFSPolicy string
ForgeKind string
}
SnapshotEntryGit mirrors the git_repos row attached to a namespace entry. CloneFilter/SparseConfig are deliberately omitted: like the event apply path, import re-derives the default blobless filter and a bootstrapped device re-derives sparse config on materialization.
type SnapshotTombstone ¶
type SnapshotTombstone struct {
PathKey string
TombstoneHLC int64
SourceEventDeviceID string
SourceEventID string
}
SnapshotTombstone is one surviving deleted entry (kept so a stale add cannot resurrect a deleted path on a bootstrapped device).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) ActiveKeyEpochAge ¶
ActiveKeyEpochAge returns the ACTIVE (highest) WCK epoch and when this device first recorded a key at it (P4-SEC-07 periodic rotation). When several kids coexist at the epoch (P6-SEC-02 collision), the EARLIEST created_at wins — the conservative choice: the rotation clock starts at the first key this device saw at the epoch, so coexisting kids can only make a rotation earlier, never suppress one. Epoch 0 (no key yet) returns a zero time and no error.
func (*Store) AdvanceHubCursor ¶
AdvanceHubCursor records that all events up to hlc have been applied from the given hub source (EAGER-02). It only moves the cursor forward: a smaller hlc than the stored value is ignored so a re-pull of stale events cannot regress the cursor.
func (*Store) AdvanceHubDeviceCursor ¶
func (s *Store) AdvanceHubDeviceCursor(ctx context.Context, hubID, deviceID string, seq int64) error
AdvanceHubDeviceCursor records that every event from deviceID up to seq has been pulled and consumed from the given hub (P5-SYNC-01). Forward-only: a smaller seq than the stored value is ignored so a stale re-pull cannot regress the cursor.
func (*Store) AdvancePushSeqCursor ¶
AdvancePushSeqCursor advances the local device's push watermark for a hub (P5-SYNC-01). Forward-only, like AdvanceHubDeviceCursor.
func (*Store) AgentRunByID ¶
func (*Store) AllBlobRefs ¶
AllBlobRefs returns every distinct age_blob:<sha256> reference in the store (env bindings + draft snapshots) (HUB-04/HUB-05). These are the blobs that may need rewrapping on device revoke or GC when unreferenced.
func (*Store) ApprovedDeviceSigningKey ¶
func (s *Store) ApprovedDeviceSigningKey(ctx context.Context, deviceID string) (string, bool, error)
ApprovedDeviceSigningKey returns the signing public key of a locally known, APPROVED device, or ok=false when the device is unknown, not approved, or has no signing key recorded. It is the trust gate for snapshot recovery (P4-SYNC-02): a retention manifest / snapshot producer must be a locally pinned approved device before its wholesale state replacement is trusted, the same fail-closed posture VerifyRemoteEvent applies to must-verify events.
func (*Store) ApprovedRecipients ¶
ApprovedRecipients returns the age recipient public keys for the local device plus all approved remote devices (HUB-04 re-encryption recipient set).
func (*Store) BlobRefCount ¶
BlobRefCount returns a map of age_blob ref → reference count (HUB-05). A blob is safe to GC only when its count is zero and it is older than the retention/snapshot horizon (the latter gate is deferred until full-state snapshot exchange exists).
func (*Store) ChainAnchorsForFloors ¶
func (s *Store) ChainAnchorsForFloors(ctx context.Context, floors map[string]int64) ([]SnapshotChainAnchor, error)
ChainAnchorsForFloors returns, for each device in floors, the hash-chain anchor a snapshot-bootstrapped device needs to verify that device's first post-floor event (P4-HUB-11): the content hash of the event at seq = floor-1. It reads the local events table first; when that row is absent — possible only for a device this machine itself imported via snapshot, so it holds no events below the floor — it falls back to the existing sync_chain_anchors row. Devices with floor <= 1 (nothing below the floor) and devices for which neither source exists are skipped. Deterministic device order.
func (*Store) ClearAllBindingRotation ¶
ClearAllBindingRotation clears the needs_rotation flag on every binding (P5-PROD-03 `--all`), used when the operator asserts all flagged secrets have been rotated at their source. Returns the number of bindings cleared.
func (*Store) ClearPendingHubDelete ¶
ClearPendingHubDelete removes a drained entry from the queue (P5-PROD-02).
func (*Store) ClearRotationForProject ¶
ClearRotationForProject clears the needs_rotation flag on a project's env bindings (P5-PROD-03), used after `devstrap env rotate` re-captures the value at its rotated source. Returns the number of bindings cleared.
func (*Store) ConflictByID ¶
ConflictByID returns a single conflict by id (any status), used by `conflicts show`/`resolve` (PROD-06).
func (*Store) CountAgentRunsByStatus ¶
func (*Store) CountOpenConflicts ¶
func (*Store) CountSecretBindingsNeedingRotation ¶
CountSecretBindingsNeedingRotation reports how many secret values are flagged for rotation (e.g. after a device revocation).
func (*Store) CountTombstonesBelowHLC ¶
CountTombstonesBelowHLC returns how many deleted namespace entries have a tombstone HLC strictly below beforeHLC, i.e. how many rows GCTombstones would purge. It backs the `hub compact --dry-run` tombstone-GC preview without mutating state.
func (*Store) CurrentHLC ¶
CurrentHLC returns the local device's current HLC clock without minting an event (P4-HUB-11): the max of the recorded device clock and the highest event HLC held in the workspace. A produced snapshot is stamped with this so that, after an importer applies ReceiveRemoteHLC(snap.HLC), every later local event sorts after everything the snapshot covers.
func (*Store) CurrentKeyEpoch ¶
CurrentKeyEpoch returns the highest WCK epoch this device holds a key for (P4-SEC-07), or 0 if none has been bootstrapped. The active epoch is the one under which new outgoing namespace events are envelope-encrypted; revoke rotates it to epoch+1 for forward secrecy.
func (*Store) DraftBlobRefs ¶
DraftBlobRefs returns the distinct age_blob refs held by draft snapshots (P5-SEC-04). These are the only blobs synced through the hub.
func (*Store) DraftProjectLimits ¶
DraftProjectLimits returns the per-project max_bytes and max_files for a draft project (DRAFT-04). Defaults are applied when no row exists.
func (*Store) DraftSnapshotsForBlobRef ¶
func (s *Store) DraftSnapshotsForBlobRef(ctx context.Context, ref string) ([]DraftSnapshotRef, error)
DraftSnapshotsForBlobRef returns the (namespace, path, size, count) of every active draft snapshot referencing ref (P5-SEC-01), so a rewrap can emit a superseding event carrying the new ref before the old hub blob is deleted.
func (*Store) EnsureDevice ¶
func (*Store) EnsureDraftProject ¶
EnsureDraftProject creates a draft_projects row for a namespace if one does not exist, so limits and snapshot pointers are available (DRAFT-02/04).
func (*Store) EnsureRemoteDevice ¶
EnsureRemoteDevice creates a placeholder device row for a remote device if it does not exist, so events from that device can satisfy the events FK constraint. The placeholder has trust_state='pending' and no keys; it is enriched via `devices enroll` before its events are signature-verified.
func (*Store) EnsureWorkspace ¶
func (*Store) EnsureWorkspaceWithID ¶
func (s *Store) EnsureWorkspaceWithID(ctx context.Context, workspaceID, name, rootPath string) error
EnsureWorkspaceWithID adopts an explicitly supplied workspace id (P4-SEC-07 pairing: a joiner adopts the founder's id at init so both devices read the same hub prefix). The id must be born-correct — the singleton workspace index plus ON DELETE CASCADE on the child tables make post-hoc rewriting hazardous, so a store already initialized under a different id is refused with ErrWorkspaceIDMismatch rather than rewritten.
func (*Store) EnvBlobRefs ¶
EnvBlobRefs returns the distinct age_blob refs held by encrypted env bindings (P5-SEC-04). These blobs are local-only and are never pushed to the hub, so the revoke rewrap must not upload or delete them there.
func (*Store) EnvProfileForProject ¶
func (s *Store) EnvProfileForProject(ctx context.Context, namespaceID string) (EnvProfile, []SecretBinding, error)
func (*Store) EventByID ¶
EventByID returns a single event by id. Used by conflict resolution (P5-SYNC-04) to recover the full payload of a losing variant so a chosen remote can be re-asserted with a fresh dominating event.
func (*Store) ForeignKeyCheck ¶
func (*Store) GCTombstones ¶
GCTombstones permanently removes deleted namespace entries whose tombstone HLC is strictly below beforeHLC. Callers must pass the minimum HLC that every approved sync cursor has already passed, so no peer can still resurrect the entry with a stale add. Its first production caller is `hub compact --gc-tombstones`, which derives beforeHLC as the minimum HLC watermark across every approved device's signed sync ack (P4-SYNC-06). Returns the number of rows purged.
func (*Store) GetLocalMeta ¶
GetLocalMeta reads a local, never-synced key/value metadata row (migration 00019), returning ok=false when the key is absent. Used by snapshot recovery to cache the highest verified per-device retention floor (P4-SYNC-02).
func (*Store) HasHubDeviceCursors ¶
HasHubDeviceCursors reports whether ANY per-device cursor row (pull or push) with a non-zero position exists for the given hub (P5-SYNC-01 founder gate): a device that has ever consumed hub content must never self-found a workspace key (P6-SEC-02).
func (*Store) HeldKeyEpochs ¶
HeldKeyEpochs returns every distinct epoch this device holds at least one WCK for, ascending (P4-SEC-07). Used by devices approve to wrap every held epoch's WCK to a newly-approved device so it can decrypt the entire history.
func (*Store) HeldKeys ¶
HeldKeys returns every (epoch, kid) this device holds a WCK for, ordered by epoch then kid (P6-SEC-02).
func (*Store) HubCursor ¶
HubCursor returns the last HLC applied from the given hub source (EAGER-02). Returns 0 when no cursor exists yet (a fresh device pulls from the beginning).
func (*Store) HubDeviceCursors ¶
HubDeviceCursors returns the per-origin-device transport cursor for a hub (P5-SYNC-01): device_id -> highest contiguous per-device seq pulled AND consumed. Devices with no row are absent (cursor 0 — pull from the beginning). Push-watermark rows live under a "push:<hubID>" hub_id and are not returned here.
func (*Store) InsertAgentRun ¶
func (*Store) InsertConflict ¶
func (*Store) InsertLocalEvent ¶
func (*Store) InsertLocalEventTx ¶
InsertLocalEventTx stamps, signs, and inserts a local event in tx.
func (*Store) InsertWorktree ¶
func (*Store) KeyCustody ¶
KeyCustody reports the recorded key-custody backend, or devicekeys.CustodyUnset when init has not recorded one yet (P6-XP-04).
func (*Store) LatestDraftSnapshot ¶
func (s *Store) LatestDraftSnapshot(ctx context.Context, namespaceID string) (*DraftSnapshot, error)
LatestDraftSnapshot returns the most recent draft bundle snapshot for a project, or nil with no error when no snapshot exists (DRAFT-02).
func (*Store) ListAgentRuns ¶
func (*Store) ListProjects ¶
func (s *Store) ListProjects(ctx context.Context) ([]ProjectStatus, error)
func (*Store) ListWorktrees ¶
func (*Store) LocalPendingEvents ¶
LocalPendingEvents returns events originated by the local device with HLC strictly greater than afterHLC (SYNC-04). It bounds the push side of sync so a cycle re-uploads only new local-origin events, not the entire event log (including remote-origin events the hub already holds from their origin device). The push cursor is stored per hub as a "push:<hubID>" row in hub_cursors.
func (*Store) LocalPendingEventsBySeq ¶
LocalPendingEventsBySeq returns events originated by the local device with Seq strictly greater than afterSeq, in Seq order (P5-SYNC-01 push side). Seq is the authoritative gapless per-device key, so unlike the retired `hlc >` selection this can never strand an event behind the watermark.
func (*Store) MarkEncryptedBindingsNeedingRotation ¶
MarkEncryptedBindingsNeedingRotation flags every encrypted secret binding as requiring value rotation. It is invoked when a device is revoked or marked lost: that device could decrypt any blob it was a recipient of, so the values must be rotated at their source — rewrapping recipients alone does not revoke historical access. Returns the number of bindings flagged.
func (*Store) MarkWorktreeRemoved ¶
func (*Store) NoteMissingKeyGrant ¶
func (s *Store) NoteMissingKeyGrant(ctx context.Context, epoch int64, kid string) (time.Time, error)
NoteMissingKeyGrant records (idempotently) that an event sealed under (epoch, kid) could not be decrypted because the key is not held, and returns the STABLE start of that epoch's grace window (P6-SEC-03). The returned time is the earliest first_seen_at across every kid recorded at the epoch — never reset by later sightings — so re-pulls cannot restart the window, and a hostile hub relabeling the unauthenticated envelope kid hint on each pull (the forged-kid stall from the P6-SEC-03 audit note) cannot mint a fresh window per label: the epoch's clock keeps running from the first sighting.
func (*Store) NoteSkippedEvent ¶
NoteSkippedEvent records (idempotently) that an event was dropped by the pull for the given reason and returns the STABLE first-seen time of that (event, reason) — re-sightings on later pulls never restart the clock (P6-SYNC-02, mirroring NoteMissingKeyGrant).
func (*Store) OpenConflicts ¶
func (*Store) OpenConflictsByType ¶
func (*Store) OpenKeyGrantWaits ¶
func (s *Store) OpenKeyGrantWaits(ctx context.Context) ([]KeyGrantWait, error)
OpenKeyGrantWaits lists every still-open key-grant wait, ordered by epoch then kid (P6-SEC-03). Rows are cleared by RecordKeyEpoch when the key arrives, so anything returned here is a key this device has seen ciphertext for and still cannot decrypt — surfaced by `doctor` as "awaiting key grants" and consulted by the `devices approve` epoch-contiguity guard.
func (*Store) OpenSkippedEvents ¶
func (s *Store) OpenSkippedEvents(ctx context.Context) ([]SkippedEvent, error)
OpenSkippedEvents lists every still-open skipped-event record, oldest first (P6-SYNC-02). Rows clear when their event finally applies (Tx.ClearSkippedEventTx from the apply path), so anything returned here is an object the hub is still serving that this device still cannot consume — surfaced by `status` and `doctor`, and a `hub gc` sweep refusal.
func (*Store) PendingHubDeletes ¶
PendingHubDeletes returns the queued orphaned blob refs (P5-PROD-02).
func (*Store) ProjectByID ¶
func (*Store) ProjectByPath ¶
func (*Store) PruneDraftSnapshots ¶
PruneDraftSnapshots deletes superseded draft snapshot rows, keeping the most recent `keep` per project (P5-HUB-02). RecordDraftSnapshot only ever INSERTs, so without pruning every superseded snapshot keeps its blob "referenced" forever and neither local nor hub GC can reclaim a stale draft blob. keep is clamped to >= 1 so the current snapshot (highest HLC) is always retained. Returns the number of rows pruned.
func (*Store) PushSeqCursor ¶
PushSeqCursor returns the local device's push watermark for a hub as a Seq (P5-SYNC-01, replacing the SYNC-04 HLC watermark, whose `hlc >` selection would silently strand events if the local HLC ever regressed relative to seq order). There is deliberately NO backfill from the legacy HLC watermark: inferring "already pushed" from `hlc <= watermark` would bake in the exact loss mode this cursor fixes — an unpushed local event stamped with a regressed HLC below the old watermark would be marked pushed forever (post-#59 Codex review, P2). A fresh watermark of 0 merely re-pushes local history once — idempotent per event ID (conditional-put dedup) and an opportunistic re-key of this device's legacy-layout events into the seq-keyed layout — never lossy.
func (*Store) QueuePendingHubDelete ¶
QueuePendingHubDelete records a blob ref orphaned by a local-only revoke rewrap so the next hub-enabled sync/gc deletes it (P5-PROD-02). Idempotent.
func (*Store) RecordDraftSnapshot ¶
func (s *Store) RecordDraftSnapshot(ctx context.Context, namespaceID, blobRef string, byteSize, fileCount int64, event Event) error
RecordDraftSnapshot inserts a draft bundle snapshot and points the project's current_snapshot_id at it (DRAFT-02). It is idempotent on source_event_id: re-applying the same event does not create a duplicate.
func (*Store) RecordKeyCustody ¶
RecordKeyCustody records the key-custody decision once (P6-XP-04). A decision already on disk is left untouched — custody is chosen at init and honored thereafter, never silently rewritten.
func (*Store) RecordKeyEpoch ¶
RecordKeyEpoch records that this device holds a WCK for (epoch, kid) (idempotent) (P4-SEC-07 / P6-SEC-02). The secret key itself is stored in the keychain via devicekeys.HybridStore.StoreWCK; this row is the non-secret local metadata. kid is the 64-lowercase-hex-char fingerprint hex(sha256(wck)); origin must be "self" (founder bootstrap or rotate), "grant" (a verified device.key.granted event), or "legacy" (migration backfill) — those are the only three paths permitted to write a row here (P6-SEC-01c).
func (*Store) RecordKeyGrant ¶
func (s *Store) RecordKeyGrant(ctx context.Context, epoch int64, kid string, recipient, sourceEventID string, sourceEventHLC int64, sourceEventDeviceID string) error
RecordKeyGrant records a workspace key grant: a WCK epoch was age-wrapped to recipient by a device.key.granted event (P4-SEC-07). Idempotent on (workspace_id, epoch, recipient) so a re-delivered grant is a no-op. This is a membership audit trail only; the wrapped key rides the event log. kid is the non-secret fingerprint of the granted WCK (P6-SEC-02) and may be empty for legacy grants.
func (*Store) RenameDevice ¶
func (*Store) ResolveConflict ¶
ResolveConflict marks a conflict resolved and records the chosen resolution (PROD-06). The resolution_json captures the user's keep-local/keep-remote/ keep-both decision for audit and cross-device sync.
func (*Store) RetainedBlobRefs ¶
RetainedBlobRefs returns the blob refs that remain referenced AFTER pruning draft snapshots to the latest `keep` per project (P5 review): env binding refs plus the top-`keep` draft snapshot refs per namespace. `hub gc --dry-run` uses this so its preview matches what a real run (prune + delete) would leave referenced, instead of counting soon-to-be-pruned superseded snapshots as live.
func (*Store) RunningAgentRunsWithPID ¶
func (*Store) SaveCapturedEnvProfile ¶
func (*Store) SaveProviderEnvProfile ¶
func (*Store) SetDevicePublicKey ¶
func (*Store) SetDeviceSigningPublicKey ¶
func (*Store) SetDeviceTrustState ¶
func (*Store) SetLocalMeta ¶
SetLocalMeta upserts a local, never-synced key/value metadata row. Unlike RecordKeyCustody's write-once semantics, this overwrites so the cached retention floor can advance (P4-SYNC-02).
func (*Store) SetProjectForgeKind ¶
SetProjectForgeKind persists a per-project forge override (GIT-05) so a self-hosted GitLab/Gitea instance routes to glab/tea instead of degrading to a compare URL. An empty kind clears the override (fall back to detection).
func (*Store) SkeletonProjects ¶
func (s *Store) SkeletonProjects(ctx context.Context) ([]ProjectStatus, error)
SkeletonProjects returns all active projects whose local materialization state is "skeleton" or "failed" — the set the eager materialization pass (EAGER-01) must touch. A re-run only revisits projects that still need work, making the pass idempotent and resumable (EAGER-04).
func (*Store) SnapshotEntries ¶
func (s *Store) SnapshotEntries(ctx context.Context) ([]SnapshotEntry, error)
SnapshotEntries returns every active namespace entry with its git_repos row and latest draft pointer, carrying the source-event coordinates that make import a pure LWW merge (P4-HUB-11). Entries are returned in path order for a deterministic snapshot document.
func (*Store) SnapshotTombstones ¶
func (s *Store) SnapshotTombstones(ctx context.Context) ([]SnapshotTombstone, error)
SnapshotTombstones returns every deleted namespace entry that carries a tombstone HLC, ordered by path_key (P4-HUB-11). A snapshot ships these so a bootstrapped device blocks a stale add from resurrecting a deleted path.
func (*Store) UpdateAgentRunResult ¶
func (*Store) UpdateAgentRunStatus ¶
func (*Store) UpdateBlobRef ¶
UpdateBlobRef repoints every reference from oldRef to newRef across secret_bindings and draft_snapshots (HUB-04 re-encryption).
func (*Store) UpdateGitDefaultBranch ¶
func (*Store) UpdateKeyKid ¶
UpdateKeyKid transactionally upgrades a legacy kid="" row at epoch to the given kid, preserving its origin and created_at (P6-SEC-02). Used once a caller computes the fingerprint for a key that was recorded before kids existed. A no-op (beyond deleting the legacy row) if a row for (epoch, kid) already exists.
func (*Store) UpdateProjectLocalState ¶
func (*Store) UpsertProject ¶
func (s *Store) UpsertProject(ctx context.Context, params UpsertProjectParams) (NamespaceEntry, error)
func (*Store) VerifyRemoteEvent ¶
VerifyRemoteEvent runs the SAME permanent-verification checks insertEvent enforces — content-hash self-consistency then signature/trust — WITHOUT inserting. It is the EncryptedHub grant-ingestion seam (P6-SEC-01) so a WCK is never written from a carrier the apply path would reject: the pre-ingest gate rejects exactly the set of events that would land in the event_verification_ failure quarantine, so the keyring can never advance from an event that never enters the log. Returns nil during the pre-enrollment bootstrap window for non-destructive events (the documented P4-SEC-04 residual); fails closed once any device is approved.
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
func (*Tx) ClearSkippedEventTx ¶
ClearSkippedEventTx deletes every skip record for an event that finally applied (P6-SYNC-02): a post-upgrade pull consuming a once-unknown-version event, or a hub replacing a garbled object with the real one, closes the wedge and the record with it. Idempotent no-op when no record exists.
func (*Tx) EnsureRemoteDeviceTx ¶
EnsureRemoteDeviceTx is the transaction-scoped version of EnsureRemoteDevice.
func (*Tx) InsertConflict ¶
func (*Tx) ProjectByPath ¶
func (*Tx) ProjectByPathKey ¶
ProjectByPathKey reads a project by its case-folded path_key within the transaction (P4-SYNC-02 snapshot import: tombstones/entries carry path_key). Returns the same "unknown namespace path" error as ProjectByPath when no active row exists.
func (*Tx) ReceiveRemoteHLC ¶
ReceiveRemoteHLC advances the local device clock to be causally after a received remote HLC (standard HLC receive rule), so future local events sort after events already observed. It assumes the caller has already rejected remote timestamps beyond the trusted skew.
func (*Tx) RecordDraftSnapshotTx ¶
func (tx *Tx) RecordDraftSnapshotTx(ctx context.Context, namespaceID, blobRef string, byteSize, fileCount int64, event Event) error
RecordDraftSnapshotTx records a draft bundle snapshot within the current transaction (DRAFT-02). It ensures a draft_projects row exists, inserts the snapshot (idempotent on source_event_id), and points draft_projects.current_snapshot_id at it.
func (*Tx) RecordKeyGrantTx ¶
func (tx *Tx) RecordKeyGrantTx(ctx context.Context, epoch int64, kid string, recipient string, event Event) error
RecordKeyGrantTx records a workspace key grant audit row transactionally with the event insert that carried it (P4-SEC-07). Idempotent on (workspace_id, epoch, recipient) so a re-delivered grant is a no-op. This is the membership audit trail only; the age-wrapped WCK rides the event payload and the secret WCK lives in the keychain (ingested by the decorator on the recipient device). Runs on every device that applies the grant event. kid is the non-secret fingerprint of the granted WCK (P6-SEC-02) and is audit metadata only; it may be empty for legacy grants.
func (*Tx) RenameProject ¶
func (tx *Tx) RenameProject(ctx context.Context, oldPath, newPath string, event Event) (RenameOutcome, error)
RenameProject moves an active namespace entry from oldPath to newPath, re-keying path_key. It returns a RenameOutcome so the caller can record a conflict on a target collision rather than overwriting.
func (*Tx) ResolveConflict ¶
ResolveConflict marks a conflict resolved inside the caller's transaction.
func (*Tx) ResolveConflictByFingerprint ¶
func (tx *Tx) ResolveConflictByFingerprint(ctx context.Context, typ, detailsJSON, resolutionJSON string) error
ResolveConflictByFingerprint marks the open conflict matching the stable (workspace_id, type, details_json) fingerprint resolved (PROD-06). Used by the conflict.resolved event apply handler so cross-device convergence does not depend on per-device conflict IDs. P5-SYNC-02: namespace_id is a locally-minted prj_<uuid> that differs per device, so it must NOT be part of the match — details_json already embeds the stable path and event-coordinate winner/loser, making (type, details_json) globally unique. It is idempotent: a duplicate event for an already-resolved (or absent) row affects zero rows and returns nil.
func (*Tx) ResolveOpenConflictsByEventID ¶
func (tx *Tx) ResolveOpenConflictsByEventID(ctx context.Context, typ, eventID, resolutionJSON string) error
ResolveOpenConflictsByEventID marks every open conflict of the given type whose details_json names event_id resolved (P6-SEC-03). Unlike ResolveConflictByFingerprint it does not need the full details string — hash-chain-break details embed the volatile cause error, so an exact fingerprint cannot be reconstructed when the event finally applies. Idempotent: zero matching rows is a no-op.
func (*Tx) TombstoneByPathKey ¶
TombstoneByPathKey tombstones an entry addressed only by its case-folded path_key (P4-SYNC-02 snapshot import: a snapshot tombstone carries no display path). When a local row for the path_key exists its display path is preserved; when none exists a deleted placeholder is created keyed on the path_key so a later stale add cannot resurrect the path.
func (*Tx) TombstoneHLC ¶
func (*Tx) TombstoneProject ¶
func (*Tx) UpsertChainAnchor ¶
func (tx *Tx) UpsertChainAnchor(ctx context.Context, deviceID string, anchorSeq int64, contentHash string, anchorHLC int64, snapshotSHA string) error
UpsertChainAnchor records a per-device hash-chain anchor imported from a snapshot (P4-SYNC-02): the content hash of the last event the snapshot covers for an origin device (at seq = floor-1), so the prev-hash verification of that device's first post-floor event has a fallback predecessor. Keeps the HIGHEST anchor_seq on conflict — a later snapshot's floor only ever moves forward, so a stale re-import must never lower a device's anchor.
func (*Tx) UpsertProject ¶
func (tx *Tx) UpsertProject(ctx context.Context, params UpsertProjectParams) (NamespaceEntry, error)
type UpsertProjectParams ¶
type UpsertProjectParams struct {
Path string
Type string
RemoteURL string
RemoteKey string
DefaultBranch string
LFSPolicy string
MaterializationPolicy string
LocalPath string
MaterializationState string
DirtyState string
SourceEventHLC int64
SourceEventDeviceID string
SourceEventID string
ForgeKind string
}
type Worktree ¶
type Worktree struct {
ID string `json:"id"`
NamespaceID string `json:"namespace_id"`
DeviceID string `json:"device_id"`
Path string `json:"path"`
Branch string `json:"branch"`
BaseRef string `json:"base_ref"`
BaseSHA string `json:"base_sha"`
CreatedBy string `json:"created_by"`
Status string `json:"status"`
DirtyState string `json:"dirty_state"`
}