Documentation
¶
Overview ¶
Package capture builds and uploads context-sync session captures. It owns the wire contract for POST /api/v1/contexts/sessions, the canonical content hash, the non-interactive capture credential, and the ingest client.
Index ¶
Constants ¶
const ( ScopeCapture = "capture" ScopeWrite = "write" )
Key scopes recorded in the credential file so `sync doctor` can detect a write-scope fallback credential and swap it for a least-privilege capture key once the backend accepts the capture scope.
const IngestPath = "/api/v1/contexts/sessions"
IngestPath is the canonical v1 ingest endpoint.
Variables ¶
This section is empty.
Functions ¶
func CredentialPath ¶
CredentialPath returns the 0600 env file path for a workspace's capture key.
func SaveCredential ¶
func SaveCredential(workspace string, cred Credential) (string, error)
SaveCredential writes the capture credential for a workspace, creating the 0700 dir and a 0600 env file the hook can source.
Types ¶
type Credential ¶
type Credential struct {
PublicKey string
SecretKey string
// Scope is the minted key's scope ("capture", or "write" for the
// broader-than-intended fallback). "" for legacy/manually written files.
Scope string
}
Credential is the workspace-bound, least-privilege capture key the detached hook uses. It is NEVER the OS keychain (DX-3): a detached background process cannot reliably unlock the keychain, so `sync init` mints this key and stores it as a 0600 file in a 0700 per-user dir that `sync run` reads directly.
func LoadCredential ¶
func LoadCredential(workspace string) (*Credential, error)
LoadCredential reads a workspace's capture credential. A missing file returns (nil, nil) so `sync run` can degrade to spooling rather than failing.
type IngestRequest ¶
type IngestRequest struct {
WorkspaceID string `json:"workspaceId"`
DirectoryID string `json:"directoryId,omitempty"`
ClaudeSessionID string `json:"claudeSessionId"`
Source string `json:"source"`
CaptureMode Mode `json:"captureMode"`
Summary string `json:"summary,omitempty"`
Metadata Metadata `json:"metadata"`
ContentHash string `json:"contentHash"`
OccurredAt string `json:"occurredAt"`
// RedactionApplied records whether client-side redaction actually replaced
// any secret span in this capture (provenance for the governance UI).
RedactionApplied bool `json:"redactionApplied"`
// LowSignal is a top-level governance routing hint (FR-Q4): true when the
// session has no real user turn and no tool work. The backend maps it to
// governance_status='low_signal' so it is excluded from the default inbox.
// It is deliberately a sibling of summary/occurredAt — NOT under metadata —
// and is excluded from the canonical content hash so it never perturbs dedup.
LowSignal bool `json:"lowSignal,omitempty"`
}
IngestRequest is the POST /api/v1/contexts/sessions body. Field names match the canonical v1 contract exactly.
func (*IngestRequest) ComputeContentHash ¶
func (r *IngestRequest) ComputeContentHash() string
ComputeContentHash computes the canonical content hash for idempotency. The server recomputes and validates this (SEC-9: a client hash is advisory), but the CLI sends a matching value so retries dedupe deterministically.
The hash covers the semantic content (session id, source, mode, summary, metadata) and deliberately excludes occurredAt so a re-run of the same session produces the same hash.
type IngestResponse ¶
type IngestResponse struct {
Status string `json:"status"`
CaptureID string `json:"captureId"`
FileID string `json:"fileId"`
VersionID string `json:"versionId"`
GovernanceStatus string `json:"governanceStatus"`
}
IngestResponse is the server reply. Status is one of accepted | deduped | superseded | disabled_by_policy | excluded.
func Ingest ¶
func Ingest(p Poster, req *IngestRequest) (*IngestResponse, error)
Ingest POSTs a capture to the backend and returns the parsed response. It fills the canonical ContentHash before sending if the caller left it empty.
type Metadata ¶
type Metadata struct {
RepoURL string `json:"repoUrl,omitempty"`
Branch string `json:"branch,omitempty"`
HeadSha string `json:"headSha,omitempty"`
FilesTouched []string `json:"filesTouched,omitempty"`
Commands []string `json:"commands,omitempty"`
Outcome string `json:"outcome,omitempty"`
// Semantic identity fields (FR-1/FR-5/FR-7). All sanitized + redacted before
// assignment. The server persists Title→captures.title, Description→
// captures.description, TaskAtHand→captures.first_prompt, ProjectKey→
// captures.project_key, RepoSlug→captures.repo_slug, and prefers these over
// its own distillation when present.
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
TaskAtHand string `json:"taskAtHand,omitempty"`
ProjectKey string `json:"projectKey,omitempty"`
RepoSlug string `json:"repoSlug,omitempty"`
}
Metadata is the structural provenance block sent with every capture. Field names are camelCase to match the canonical v1 wire contract.