Documentation
¶
Overview ¶
Package evidence implements the local evidence loop behind `csx run` (goal.md §8.3, contract C14): scan the project, run the user's command, record aggregate observations for PUBLIC packages only, and drain them into anonymous wire batches. Raw logs and paths never enter any uploadable structure; PRIVATE and UNKNOWN packages never enter the observations table at all.
Index ¶
- Constants
- Variables
- func PrepareWantedForUpload(ctx context.Context, payload string, ...) ([]byte, error)
- func QueueWanted(ctx context.Context, db *localdb.DB, ident *identity.Identity, ...)
- func Run(ctx context.Context, argv []string, dir string) (exitCode int, stderrTail string, err error)
- func Scan(ctx context.Context, dir string, checker *registry.Checker) (*scanner.ScanResult, error)
- type Batcher
- type PublicnessCache
- type Recorder
- type WantedReport
Constants ¶
const WantedCandidateQueueKind = "wanted_candidate"
WantedCandidateQueueKind is deliberately not the legacy "wanted" kind. Older daemons know how to POST "wanted" rows without a client-side publicness recheck. Giving unverified local candidates a new kind means an upgraded MCP process can safely share a home with an older daemon: the older process leaves the unfamiliar row alone instead of transmitting it.
Variables ¶
var ErrWantedPublicnessUnconfirmed = errors.New("wanted package publicness is unconfirmed")
ErrWantedPublicnessUnconfirmed means a candidate was syntactically safe to keep locally, but no package in it was confirmed public. The queue drainer treats this as retryable because a registry outage and a private/nonexistent coordinate are intentionally indistinguishable at this privacy boundary.
Functions ¶
func PrepareWantedForUpload ¶ added in v0.1.5
func PrepareWantedForUpload(ctx context.Context, payload string, isPublic func(context.Context, domain.PURL) bool) ([]byte, error)
PrepareWantedForUpload applies the last, fail-closed privacy gate at the point where a queued candidate is about to leave the machine. It decodes into the fixed wire type and marshals it again, so even a hand-edited queue row cannot smuggle an unknown query/path/log field through the daemon.
isPublic must check the exact package version. A nil checker or a false result never transmits that coordinate. The server repeats this check on receipt; neither side treats PURL syntax as proof of public existence.
func QueueWanted ¶ added in v0.1.4
func QueueWanted(ctx context.Context, db *localdb.DB, ident *identity.Identity, cfg *config.Config, req domain.SearchRequest)
QueueWanted records that the network had no answer, so the question can be counted and eventually answered by someone.
It goes through the upload queue rather than straight to the network: the queue retries, works offline, and a search must never wait on a report. It lives here rather than in the daemon because AGENTS ARRIVE OVER MCP — the daemon's HTTP search is not the path that matters, and wiring the signal only there meant the one caller who actually asks questions was the one caller whose misses were thrown away.
THE QUESTION IS NEVER SENT. A typed question carries project names, file paths, sometimes an error string with a hostname in it, and goal.md §8.5 keeps all of that on the machine. The local candidate contains only package coordinates and an unambiguous symbol; the daemon still must confirm the exact release on its public registry before any of it is eligible to travel.
func Run ¶
func Run(ctx context.Context, argv []string, dir string) (exitCode int, stderrTail string, err error)
Run spawns argv[0] with the remaining args in dir, inheriting stdio so the wrapped command behaves exactly as if run directly. stderr is additionally teed into a bounded in-memory ring (returned as stderrTail, capture only — the caller sanitizes before anything is stored in an uploadable row) and appended raw to $CSX_HOME/logs/last-run.log, which never leaves the machine.
The child's exit code passes through: a non-zero child exit is NOT an error here (exitCode carries it, err stays nil). err is reserved for failures to run at all (command not found, ...).
func Scan ¶
Scan detects the ecosystems present in dir and runs the scan orchestrator (contract C14 step 1). A nil checker skips the publicness pass — packages stay UNKNOWN and are excluded from evidence. This wrapper lives here rather than in internal/scanner because both adapters and internal/registry import internal/scanner.
Types ¶
type Batcher ¶
Batcher drains pending local observation aggregates into anonymous wire batches (contract C14 step 6). Batches carry only the fields of domain.ObservationBatch — rotating IDs, hashes, and counts; never paths, project names, or raw logs.
func (*Batcher) Drain ¶
Drain builds wire batches from every pending observation aggregate and marks the drained rows uploaded in the same step (read and mark back-to-back, no I/O in between — the localdb contract: rows carry FULL epoch counts and any later increment flips a row back to pending, so a full-count re-send delta-merges safely server-side). Ownership of the returned batches passes to the caller.
func (*Batcher) Upload ¶
func (b *Batcher) Upload(ctx context.Context, httpClient *http.Client, serverURL string) (int, error)
Upload drains pending observations and POSTs them to {serverURL}/v1/evidence/batches as {"batches":[...]}. Community mode only: any other mode is a silent no-op. On a non-2xx response or a transport error the affected rows are restored to pending so no evidence is lost while the server is unreachable (goal.md §25.F). It returns how many batches the server accepted.
The upload is chunked to the server's documented per-request limit and repeats until the queue is empty. Posting a whole drain in one request meant any backlog larger than that limit was rejected wholesale and retried forever — the exact shape of a first sync after scanning a machine's worth of projects.
type PublicnessCache ¶
PublicnessCache adapts localdb's packages table to registry.Cache so publicness verdicts persist across runs (24h TTL enforced by the checker via the stored checked_at stamp).
func (PublicnessCache) GetPublicness ¶
GetPublicness looks up a cached verdict by canonical purl string.
func (PublicnessCache) SetPublicness ¶
func (c PublicnessCache) SetPublicness(ctx context.Context, purl, status string) error
SetPublicness stores a registry check outcome.
type Recorder ¶
Recorder turns one wrapped command run into local aggregate observations (contract C14 steps 4–5). Only PUBLIC packages ever reach the observations table; PRIVATE and UNKNOWN packages are recorded in the local packages inventory only, which is never uploaded.
func (*Recorder) RecordRun ¶
func (r *Recorder) RecordRun(ctx context.Context, dir string, res *scanner.ScanResult, profile scanner.CommandProfile, exitCode int, stderrTail string) error
RecordRun records the outcome of one `csx run` execution.
- Every scanned package is upserted into the local packages table (local diagnostics; never uploaded).
- Known commands record, per PUBLIC package, one package-level observation (symbol "") and one observation per public symbol usage, at the classified stage with PASS/FAIL from exitCode. On FAIL the stderr tail is sanitized (stage-aware) and only the resulting fingerprint + error code are attached — never the raw text.
- Unknown commands (profile.Known == false) prove nothing beyond "these public packages are in use": they record only USED/PASS package rows.
- Symbol sightings are stored with the rotating project bucket derived from the absolute project path (HMAC, irreversible).
type WantedReport ¶ added in v0.1.5
type WantedReport struct {
SchemaVersion int `json:"schemaVersion"`
Epoch string `json:"epoch"`
AnonID string `json:"anonId"`
Packages []string `json:"packages"`
Symbols []string `json:"symbols,omitempty"`
}
WantedReport is the complete privacy-reduced wire shape. It contains no prose query, project path, dependency-tree context, hostname, or raw log. Candidates use the same shape locally, but are not eligible for upload until PrepareWantedForUpload confirms and rewrites their package list.