apicontract

package
v0.26.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package apicontract holds the Go types for every shared JSON type and envelope in the core-investigation-loop Feature's normative transport appendix (spec/features/core-investigation-loop/api-contract.md, hub datatug/datatug), with the exact json:"..." field names that document declares - the schema authority datatug-cli (server) and datatug-apps (client) consume, per Phase 1 corrective Task 12.

Every type's Validate method enforces the appendix's own stated rules - required fields, closed enums, cross-field pairings, canonical string forms - so a value that unmarshals successfully but violates the contract is still caught before it crosses a trust boundary. TypedValue and Scope's carrying types additionally reject unknown fields, duplicate JSON keys and (via DecodeStrict) explicitly named security-relevant fields such as a client-supplied principal or role.

pkg/apicontract/fixtures holds the frozen JSON fixtures generated from these types - one file per envelope/error case the appendix's "Acceptance and migration" section requires coverage for.

Index

Constants

View Source
const (
	BindingOriginSelection = "selection"
	BindingOriginContext   = "context"
	BindingOriginManual    = "manual"
	BindingOriginDefault   = "default"
)
View Source
const (
	BindingOriginEvidenceServerDefault  = "server-default"
	BindingOriginEvidenceClientReported = "client-reported"
)
View Source
const (
	CandidateStateRunnable          = "runnable"
	CandidateStateNeedsInput        = "needs-input"
	CandidateStateNeedsTarget       = "needs-target"
	CandidateStateSourceUnavailable = "source-unavailable"
)
View Source
const (
	FactOriginSelection = "selection"
	FactOriginContext   = "context"
	FactOriginManual    = "manual"
)
View Source
const (
	FactMappingDeclared = "declared"
	FactMappingInferred = "inferred"
)
View Source
const (
	SemanticProvenanceDeclared = "declared"
	SemanticProvenanceInferred = "inferred"
)
View Source
const (
	ProvenanceModeLive     = "live"
	ProvenanceModeSnapshot = "snapshot"
)
View Source
const (
	ExecutionProfileProtected        = "protected"
	ExecutionProfileOpaquePrivileged = "opaque-privileged"
)

Variables

This section is empty.

Functions

func DecodeStrict

func DecodeStrict(data []byte, v any, forbidden ...string) error

DecodeStrict decodes data into v with no leniency: unknown JSON fields are rejected (encoding/json's DisallowUnknownFields), duplicate keys at any nesting level are rejected (encoding/json silently keeps only the last occurrence otherwise - the appendix requires duplicate JSON keys to be "rejected, never reconciled by precedence"), trailing data after the JSON value is rejected, and any field name listed in forbidden is rejected even when the target type has no field for it - the same "client-supplied principal or role are rejected" rule the appendix states, made explicit at every call site that carries security-relevant identity.

Types

type AgentCapabilities

type AgentCapabilities struct {
	ProtectedQueries bool `json:"protectedQueries"`
	OpaqueReadOnly   bool `json:"opaqueReadOnly"`
}

AgentCapabilities reports what the session may do - never what it did; Result.Provenance.ExecutionProfile reports the actual executor used for a given run, "even a privileged principal running protected DTQL still receives protected provenance".

type AgentInfo

type AgentInfo struct {
	Version           string            `json:"version"`
	Principal         AgentPrincipal    `json:"principal"`
	SecurityContextID string            `json:"securityContextId"`
	Projects          []AgentProjectRef `json:"projects"`
	Capabilities      AgentCapabilities `json:"capabilities"`
}

AgentInfo is the exact success envelope of GET agent-info. api-contract.md "Endpoint table".

func (AgentInfo) Validate

func (a AgentInfo) Validate() error

Validate enforces Version and SecurityContextID are required, Principal is itself valid, and every Projects entry is itself valid.

type AgentPrincipal

type AgentPrincipal struct {
	ID     string   `json:"id"`
	Roles  []string `json:"roles"`
	Groups []string `json:"groups"`
}

AgentPrincipal is the effective identity agent-info reports - never the role inferred from a name; the server states it explicitly.

func (AgentPrincipal) Validate

func (p AgentPrincipal) Validate() error

type AgentProjectRef

type AgentProjectRef struct {
	ID string `json:"id"`
}

AgentProjectRef is one project agent-info reports as registered.

func (AgentProjectRef) Validate

func (r AgentProjectRef) Validate() error

type Ambiguous

type Ambiguous struct {
	ParameterID string   `json:"parameterId"`
	FactIDs     []string `json:"factIds"`
}

Ambiguous names a parameter with more than one distinct candidate value within the highest eligible automatic binding tier.

func (Ambiguous) Validate

func (a Ambiguous) Validate() error

type ApplicableResponse

type ApplicableResponse struct {
	Applicable []Candidate `json:"applicable"`
	NotYet     []Candidate `json:"notYet"`
}

ApplicableResponse is the exact success envelope of POST queries/applicable. "applicable contains only runnable candidates. notYet contains authorized query metadata for needs-input/needs-target/ source-unavailable candidates... Candidates sort by queryId for deterministic tests." api-contract.md "Endpoint table".

func (ApplicableResponse) Validate

func (r ApplicableResponse) Validate() error

Validate enforces every candidate is itself valid, every Applicable entry has State "runnable", every NotYet entry does not, and both slices are sorted by QueryID.

type Binding

type Binding struct {
	ParameterID    string     `json:"parameterId"`
	Value          TypedValue `json:"value"`
	Origin         string     `json:"origin"`         // selection | context | manual | default
	OriginEvidence string     `json:"originEvidence"` // server-default | client-reported
	FactID         string     `json:"factId,omitempty"`
}

Binding is one parameter's resolved value and its provenance. Returned bindings are execution-confirmed; a selection/context/manual origin's OriginEvidence remains explicitly "client-reported", never presented as server-attested; "server-default" is used only when a declared default was validated against the query definition. api-contract.md "Shared JSON types" / "Endpoint table".

func (Binding) Validate

func (b Binding) Validate() error

Validate enforces ParameterID is required, Value is itself valid, Origin and OriginEvidence are each one of their closed sets, and the two pair correctly: Origin "default" only with OriginEvidence "server-default", every other Origin only with "client-reported" - "The UI must not present client origins as server-attested provenance."

type BindingOriginEntry

type BindingOriginEntry struct {
	ParameterID string `json:"parameterId"`
	Origin      string `json:"origin"` // selection | context | manual | default
	FactID      string `json:"factId,omitempty"`
}

BindingOriginEntry supplies display provenance for one submitted parameter - "never influences authorization". api-contract.md "Endpoint table".

func (BindingOriginEntry) Validate

func (e BindingOriginEntry) Validate() error

type Candidate

type Candidate struct {
	QueryID string            `json:"queryId"`
	Targets []CandidateTarget `json:"targets"`
	// SelectedSource is present only when exactly one eligible target
	// remains - "present only when one eligible target remains".
	SelectedSource string      `json:"selectedSource,omitempty"`
	Bindings       []Binding   `json:"bindings"`
	Chain          []ChainStep `json:"chain"`
	Missing        []string    `json:"missing"`
	Ambiguous      []Ambiguous `json:"ambiguous"`
	State          string      `json:"state"`
}

Candidate is one query's runnability against the caller's current facts: its authorized eligible targets, the bindings/chain it would apply, what is still missing or ambiguous, and its overall State. api-contract.md "Endpoint table".

func (Candidate) Validate

func (c Candidate) Validate() error

Validate enforces QueryID is required, every Target/Binding/ChainStep/ Ambiguous entry is itself valid, SelectedSource is set only when there is exactly one Target, and State is one of the closed set.

type CandidateTarget

type CandidateTarget struct {
	Source string `json:"source"`
	Label  string `json:"label"`
}

CandidateTarget is one authorized eligible source a saved query could run against.

func (CandidateTarget) Validate

func (t CandidateTarget) Validate() error

type ChainStep

type ChainStep struct {
	ParameterID string `json:"parameterId"`
	FactID      string `json:"factId,omitempty"`
	Explanation string `json:"explanation"`
}

ChainStep explains, per parameter, how its value was (or would be) derived - declared, inferred or manual - distinctly. "a missing parameter still has an explanation."

func (ChainStep) Validate

func (c ChainStep) Validate() error

type Column

type Column struct {
	Name string `json:"name"`
	Type string `json:"type"`
}

Column names one recordset column and its declared type.

func (Column) Validate

func (c Column) Validate() error

type ErrorBody

type ErrorBody struct {
	Code      string         `json:"code"`
	Message   string         `json:"message"`
	Field     string         `json:"field,omitempty"`
	RequestID string         `json:"requestId"`
	Targets   []TargetOption `json:"targets,omitempty"`
}

ErrorBody is the exact shape of every error's "error" field. "Messages and IDs must not reveal protected values or credentials. Tests assert both status and code, not English wording." api-contract.md "Security and errors".

func (ErrorBody) Validate

func (e ErrorBody) Validate() error

Validate enforces Code is one of the closed ErrorCode set, Message and RequestID are required, and Targets is populated only when Code is TARGET_REQUIRED - "Only TARGET_REQUIRED may include authorized target options."

type ErrorCode

type ErrorCode string

ErrorCode is the closed set of error codes api-contract.md "Security and errors" declares, each mapped to its exact HTTP status.

const (
	ErrCodeInvalidRequest                ErrorCode = "INVALID_REQUEST"
	ErrCodeTypeMismatch                  ErrorCode = "TYPE_MISMATCH"
	ErrCodeMissingParameter              ErrorCode = "MISSING_PARAMETER"
	ErrCodeAmbiguousBinding              ErrorCode = "AMBIGUOUS_BINDING"
	ErrCodeTargetRequired                ErrorCode = "TARGET_REQUIRED"
	ErrCodeUnauthenticated               ErrorCode = "UNAUTHENTICATED"
	ErrCodeAccessDenied                  ErrorCode = "ACCESS_DENIED"
	ErrCodeUnsupportedProtectedExecution ErrorCode = "UNSUPPORTED_PROTECTED_EXECUTION"
	ErrCodeNotFound                      ErrorCode = "NOT_FOUND"
	ErrCodeStaleContext                  ErrorCode = "STALE_CONTEXT"
	ErrCodeResponseTooLarge              ErrorCode = "RESPONSE_TOO_LARGE"
	ErrCodeSourceUnavailable             ErrorCode = "SOURCE_UNAVAILABLE"
	ErrCodeTimeout                       ErrorCode = "TIMEOUT"
)

func (ErrorCode) HTTPStatus

func (c ErrorCode) HTTPStatus() int

HTTPStatus returns c's exact HTTP status, or 0 for an unknown code.

func (ErrorCode) Valid

func (c ErrorCode) Valid() bool

Valid reports whether c is one of the closed set of error codes.

type ErrorEnvelope

type ErrorEnvelope struct {
	Error ErrorBody `json:"error"`
}

ErrorEnvelope is the exact shape of every error response. "{error:{code:string,message:string,field?:string,requestId:string, targets?:{source:string,label:string}[]}}" api-contract.md "Security and errors".

func (ErrorEnvelope) Validate

func (e ErrorEnvelope) Validate() error

type ExecutionRequest

type ExecutionRequest struct {
	Project           string                `json:"project"`
	Environment       string                `json:"environment"`
	SecurityContextID string                `json:"securityContextId"`
	Source            string                `json:"source,omitempty"`
	QueryID           string                `json:"queryId,omitempty"`
	DTQL              string                `json:"dtql,omitempty"`
	Parameters        map[string]TypedValue `json:"parameters"`
	BindingOrigins    []BindingOriginEntry  `json:"bindingOrigins"`
	Mode              string                `json:"mode"` // live | snapshot
	SnapshotID        string                `json:"snapshotId,omitempty"`
	Limit             *int                  `json:"limit,omitempty"`
}

ExecutionRequest is POST exec/run_query's request body. "For ad-hoc DTQL, source is required. For saved queries, source obeys target resolution... Unknown parameters and type mismatches are rejected. The server never silently binds from stored browser context." api-contract.md "Endpoint table".

func (ExecutionRequest) Validate

func (r ExecutionRequest) Validate() error

Validate enforces: Project/Environment/SecurityContextID required; exactly one of QueryID/DTQL ("exactly one required"); DTQL set requires Source ("For ad-hoc DTQL, source is required"); every Parameters value is itself valid; BindingOrigins is checked for exactly the submitted Parameters keys - no more, no fewer - and each entry is itself valid; Mode is one of the closed set, snapshot Mode requires a SnapshotID; Limit, when present, is within (0, 500] - "Default result limit is 100 and maximum is 500."

type Fact

type Fact struct {
	ID       string       `json:"id"`
	Entity   string       `json:"entity"`
	Field    string       `json:"field"`
	Value    TypedValue   `json:"value"`
	Origin   string       `json:"origin"` // selection | context | manual
	Physical *PhysicalRef `json:"physical,omitempty"`
	Mapping  string       `json:"mapping,omitempty"` // declared | inferred
	Enabled  bool         `json:"enabled"`
}

Fact is a semantic suggestion, not an access credential - the server revalidates physical references and mapping declarations against authorized project metadata. Manual facts cannot impersonate a selected protected record. api-contract.md "Shared JSON types".

func (Fact) Validate

func (f Fact) Validate() error

Validate enforces id/entity/field are required, Value is itself valid, Origin is one of the closed set, Physical (when present) is itself valid, and Mapping (when present) is one of the closed set.

type Limitation

type Limitation struct {
	Policy        string   `json:"policy"`
	RowsFiltered  bool     `json:"rowsFiltered"`
	HiddenColumns []string `json:"hiddenColumns"`
}

Limitation reports an applied restriction, not the number of rejected rows or their values. Only names visible in authorized metadata may appear in HiddenColumns or Policy; use a generic policy label and an empty HiddenColumns where names are protected. api-contract.md "Shared JSON types" / "Limitations report applied restrictions...".

func (Limitation) Validate

func (l Limitation) Validate() error

Validate enforces Policy is required - a limitation with no named policy cannot be attributed to a rule, and the appendix requires attribution ("never silent, always attributable to a named rule").

type PhysicalRef

type PhysicalRef struct {
	Source     string `json:"source"`
	Collection string `json:"collection"`
	Column     string `json:"column"`
}

PhysicalRef names a physical column a semantic mapping points at. "PhysicalRef = {source: string; collection: string; column: string}" - api-contract.md "Shared JSON types".

func (PhysicalRef) Validate

func (r PhysicalRef) Validate() error

Validate enforces all three fields are required per the appendix's type.

type Provenance

type Provenance struct {
	Source           string `json:"source"`
	Collection       string `json:"collection,omitempty"`
	QueryID          string `json:"queryId,omitempty"`
	Mode             string `json:"mode"` // live | snapshot
	SnapshotID       string `json:"snapshotId,omitempty"`
	ObservedAt       string `json:"observedAt"`
	ExecutionProfile string `json:"executionProfile"` // protected | opaque-privileged
}

Provenance describes where a Result actually came from: "Result.provenance .executionProfile reports the actual executor used for that run" - never the session's allowed capabilities, and a privileged principal running protected DTQL still receives protected provenance. api-contract.md "Shared JSON types" / "Security and errors".

func (Provenance) Validate

func (p Provenance) Validate() error

Validate enforces Source and ObservedAt are required (ObservedAt must be an RFC3339-UTC-normalized instant, the same rule TypedValue's "datetime" type enforces), Mode is one of the closed set, snapshot Mode requires a SnapshotID ("a snapshot requires explicit separate selection... and a configured snapshotId"), and ExecutionProfile is one of the closed set.

type Recordset

type Recordset struct {
	Columns []Column       `json:"columns"`
	Rows    [][]TypedValue `json:"rows"`
}

Recordset is Result's tabular payload. "Arrays are ordered; rows have exactly one value per returned column." api-contract.md "Shared JSON types".

func (Recordset) Validate

func (r Recordset) Validate() error

Validate enforces every column is itself valid, every row has exactly as many values as there are columns, and every cell value is itself valid.

type RelatedItem

type RelatedItem struct {
	LookupID   string `json:"lookupId"`
	Label      string `json:"label"`
	Source     string `json:"source"`
	Collection string `json:"collection"`
	Count      *int64 `json:"count"` // number | null
}

RelatedItem is one related-lookup target. Count is optional: nil means "return null if an exact authorized count cannot be obtained within a 2-second budget" - never the unrestricted count.

func (RelatedItem) Validate

func (i RelatedItem) Validate() error

type RelatedResponse

type RelatedResponse struct {
	Related   []RelatedItem `json:"related"`
	Truncated bool          `json:"truncated"`
}

RelatedResponse is the exact success envelope of POST semantic/related. api-contract.md "Endpoint table".

func (RelatedResponse) Validate

func (r RelatedResponse) Validate() error

Validate enforces every Related entry is itself valid and there are no more than relatedMaxItems of them.

type Result

type Result struct {
	Recordset       Recordset    `json:"recordset"`
	Limitations     []Limitation `json:"limitations"`
	BindingsApplied []Binding    `json:"bindingsApplied"`
	Provenance      Provenance   `json:"provenance"`
	Truncated       bool         `json:"truncated"`
}

Result is the exact success envelope of POST exec/run_query and POST semantic/related/rows. "Denied requests return no recordset, sample, true count, SQL text or hidden value." api-contract.md "Shared JSON types".

func (Result) Validate

func (r Result) Validate() error

Validate enforces Recordset, every Limitation, every applied Binding and Provenance are each themselves valid.

type Scope

type Scope struct {
	Project           string `json:"project"`
	Environment       string `json:"environment"`
	SecurityContextID string `json:"securityContextId"`
}

Scope identifies the project, environment and staleness-check security context every scoped call carries. "Scope = {project: string, environment: string, securityContextId: string}" - api-contract.md "Scope and identity". Project and environment IDs are required, nonempty, and resolved within this server's registered projects; securityContextId is a staleness check, never authentication.

func (Scope) Validate

func (s Scope) Validate() error

Validate enforces "Project and environment IDs are required, nonempty" and the same for securityContextId (a call carrying a blank/absent staleness check cannot be validated against anything).

type SemanticColumnMapping

type SemanticColumnMapping struct {
	Column     string `json:"column"`
	Entity     string `json:"entity"`
	Field      string `json:"field"`
	Provenance string `json:"provenance"` // declared | inferred
}

SemanticColumnMapping is one physical column's resolved entity/field mapping. Unmapped columns are omitted from the response entirely, never listed with an empty entity/field.

func (SemanticColumnMapping) Validate

func (m SemanticColumnMapping) Validate() error

type SemanticColumnsResponse

type SemanticColumnsResponse struct {
	Columns []SemanticColumnMapping `json:"columns"`
}

SemanticColumnsResponse is the exact success envelope of GET semantic/columns. api-contract.md "Endpoint table".

func (SemanticColumnsResponse) Validate

func (r SemanticColumnsResponse) Validate() error

type SourceRef

type SourceRef struct {
	Source     string `json:"source"`
	Collection string `json:"collection"`
}

SourceRef names a stable project-local source, resolved through the project registry for the requested environment - never a filesystem path, arbitrary HTTP URL or credential. "SourceRef = {source: string, collection: string}" - api-contract.md "Scope and identity".

func (SourceRef) Validate

func (r SourceRef) Validate() error

Validate enforces both fields are required per the appendix's type.

type TargetOption

type TargetOption struct {
	Source string `json:"source"`
	Label  string `json:"label"`
}

TargetOption is one authorized eligible source offered on a TARGET_REQUIRED error - "the same authorized target options in error.targets, never hidden source IDs."

func (TargetOption) Validate

func (t TargetOption) Validate() error

type TypedValue

type TypedValue struct {
	Type ValueType
	Str  string
	Num  float64
	Bool bool
}

TypedValue is the wire tagged union every value crossing the transport boundary uses - api-contract.md "Shared JSON types". Missing and null differ (omit an unbound parameter; null satisfies only a nullable parameter); false, zero and empty string are present values, never coerced. Exactly one of Str/Num/Bool holds the payload, selected by Type: Str for string/integer/decimal/date/datetime (the wire encodes all of these as a JSON string, per the union), Num for number (a JSON number), Bool for boolean (a JSON boolean); null carries no payload.

func NewBooleanValue

func NewBooleanValue(v bool) TypedValue

func NewDateValue

func NewDateValue(v string) TypedValue

func NewDatetimeValue

func NewDatetimeValue(v string) TypedValue

func NewDecimalValue

func NewDecimalValue(v string) TypedValue

func NewIntegerValue

func NewIntegerValue(v string) TypedValue

func NewNullValue

func NewNullValue() TypedValue

func NewNumberValue

func NewNumberValue(v float64) TypedValue

func NewStringValue

func NewStringValue(v string) TypedValue

func (TypedValue) MarshalJSON

func (v TypedValue) MarshalJSON() ([]byte, error)

MarshalJSON writes the exact `{type:'...', value:...}` wire shape, with value's own JSON kind (string/number/boolean/null) selected by Type.

func (*TypedValue) UnmarshalJSON

func (v *TypedValue) UnmarshalJSON(data []byte) error

UnmarshalJSON parses the wire shape strictly: only "type" and "value" are accepted fields, at most once each (duplicate keys are checked directly - not just left to a caller's DecodeStrict - since TypedValue.UnmarshalJSON is invoked by encoding/json's own machinery whenever this type is embedded in a larger structure, bypassing any outer DecodeStrict call), value's JSON kind must match what Type requires (no string/number coercion), and the parsed result must pass Validate() before it is accepted.

func (TypedValue) Validate

func (v TypedValue) Validate() error

Validate enforces the appendix's per-type content rules: "finite, exactly representable JSON number" for number; "canonical decimal integer, no leading +/zeros" for integer; "canonical decimal; preserve precision" for decimal; "YYYY-MM-DD, valid calendar date" for date; "RFC3339 normalized to UTC" for datetime. string/boolean/null carry no further content constraint - false, zero and empty string are valid present values.

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError reports a single appendix rule a value violated.

func (*ValidationError) Error

func (e *ValidationError) Error() string

type ValueType

type ValueType string

ValueType is TypedValue's tagged-union discriminant - the closed set of "type" values api-contract.md's TypedValue union declares.

const (
	ValueTypeString   ValueType = "string"
	ValueTypeNumber   ValueType = "number"
	ValueTypeInteger  ValueType = "integer"
	ValueTypeDecimal  ValueType = "decimal"
	ValueTypeBoolean  ValueType = "boolean"
	ValueTypeDate     ValueType = "date"
	ValueTypeDatetime ValueType = "datetime"
	ValueTypeNull     ValueType = "null"
)

Directories

Path Synopsis
Package fixtures holds the frozen JSON fixtures for pkg/apicontract's normative transport types (spec/features/core-investigation-loop/ api-contract.md, hub datatug/datatug) - one file per envelope/error case listed in the appendix's "Acceptance and migration" section.
Package fixtures holds the frozen JSON fixtures for pkg/apicontract's normative transport types (spec/features/core-investigation-loop/ api-contract.md, hub datatug/datatug) - one file per envelope/error case listed in the appendix's "Acceptance and migration" section.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL