Documentation
¶
Overview ¶
Package spec defines the kit v3 descriptor: the declaration half of a kit, published as a manifest annotation on an ordinary OCI image whose layers are the kit's content.
The descriptor deliberately carries no top-level identity name, image reference, or runtime config (entrypoint, env, workdir, user, default command): identity is the reference a kit is consumed by, capability names are display labels, and runtime config lives in the image config, set by the companion Dockerfile.
Decode and Validate read a descriptor. Merge folds an ordered set of them into the one descriptor a composition publishes. Compose reconciles selected runtime declarations. SelectCapabilities evaluates atomic groups using the runtime's callback; its callers retain selected inputs for handlers that need per-Kit provenance. Ordering the set is resolve; reading the descriptors out of a registry is fetch; folding the images is assemble.
Index ¶
- Constants
- func AgentSkillName(s AgentSkill) string
- func CanonicalRequire(r Require) string
- func CapabilityTypes(capabilities []Capability) string
- func CheckReExport(kitArg string, kit Arg, setName string, set Arg, declared bool) error
- func CompareVersions(a, b string) int
- func ContainsArgRef(s string) bool
- func ContainsEnvRef(s string) bool
- func ContainsKitPlaceholder(s string) bool
- func DecodeCapabilityConfig(n Capability, out any) error
- func DerivedProvides(namespace string, listings [][]Package) []string
- func DisplayCapabilityName(name string) string
- func EntryMethods(e NetworkEntry) []string
- func EntryPaths(e NetworkEntry) []string
- func ExpandBuildArgs(raw []byte, decls map[string]Arg, values map[string]string) ([]byte, error)
- func ExpandCreateArgs(raw []byte, decls map[string]Arg, values map[string]string) ([]byte, error)
- func ExpandString(s string, values map[string]string) (string, error)
- func FormatWidenings(ws []Widening) string
- func HTTPMethods() []string
- func HasCapability(needs []Capability, typ string) bool
- func HasEnvReferences(items []Capability) bool
- func HasGroups(items []Capability) bool
- func IsDerivedProvide(name string) bool
- func IsVersion(s string) bool
- func IsWholeArgRef(s string) bool
- func KitArgValues(decls map[string]Arg, supplied map[string]string) (map[string]string, error)
- func KnownCapabilities() []string
- func LatestVersion(tags []string) string
- func NormalizeCapabilityName(name string) string
- func OCIAnnotations(d *Descriptor) map[string]string
- func OwnedProvides(contributions []Contribution) map[string][]OwnedProvide
- func PackageVersion(raw string) string
- func PortKey(p Port) string
- func PortTransport(p Port) string
- func Positions(raw []byte) map[string]Position
- func PrivilegedOf(needs []Capability) bool
- func ProvidesIndex(contributions []Contribution) map[string][]Provide
- func ReferencedArgs(raw []byte) []string
- func RequireAuthoredProvides(d *Descriptor) error
- func RequireVersionedProvides(d *Descriptor) error
- func ResolveArgs(decls map[string]Arg, supplied map[string]string) (map[string]string, error)
- func SatisfiedBy(provided map[string][]Provide, r Require) bool
- func SatisfiedByOther(owned map[string][]OwnedProvide, r Require, by string) bool
- func Satisfies(p Provide, r Require) bool
- func SkillsMode(s AgentSkills) string
- func SkillsWritable(s AgentSkills) bool
- func TagVersion(tag string) string
- func Validate(d *Descriptor) (warnings []string, err error)
- func ValidateDeclarations(raw []byte, d *Descriptor) ([]string, error)
- func ValidateEffective(raw []byte, d *Descriptor) ([]string, error)
- func ValidateExpandedDeclarations(raw []byte, d *Descriptor) ([]string, error)
- func ValidatePublished(raw []byte, d *Descriptor) (warnings []string, err error)
- func ValidateRaw(raw []byte, d *Descriptor) (warnings []string, err error)
- func WithSource(err error, filename string, raw []byte) error
- type APIKey
- type AgentContext
- type AgentSessions
- type AgentSkill
- type AgentSkillCapability
- type AgentSkills
- type AgentSkillsCapability
- type Arg
- type BuiltBy
- type Capability
- type CapabilityDecision
- type CapabilityGroup
- type CapabilityItem
- type CapabilitySource
- type CommandLine
- type Constraint
- type ConstraintOp
- type ContextSource
- type Contribution
- type Credential
- type CredentialCapability
- type CredentialFile
- type Descriptor
- type FieldError
- type File
- type HostMount
- type Inject
- type InstallHook
- type Kit
- type Lifecycle
- type MergeOptions
- type MergeResult
- type NetworkEntry
- type NetworkRules
- type NetworkRulesV2
- type OAuth
- type OwnedProvide
- type Package
- type PackageDatabase
- type PhasedNetwork
- type PhasedNetworkV2
- type Phases
- type Port
- type Position
- type Provide
- type Require
- type Resources
- type ResponseFields
- type SSHAgent
- type SSHAgentCapability
- type SSHAgentPhases
- type SelectCapability
- type Selection
- type SelectionRecord
- type Sentinels
- type StartupHook
- type Surface
- type TokenEndpoint
- type USBDevice
- type USBDeviceCapability
- type ValidationErrors
- type Volume
- type Widening
Examples ¶
Constants ¶
const ( OCIAnnotationTitle = "org.opencontainers.image.title" OCIAnnotationDescription = "org.opencontainers.image.description" OCIAnnotationAuthors = "org.opencontainers.image.authors" OCIAnnotationSource = "org.opencontainers.image.source" OCIAnnotationLicenses = "org.opencontainers.image.licenses" OCIAnnotationVersion = "org.opencontainers.image.version" )
Standard OCI image annotation keys the frontend populates from the descriptor, so generic registry tooling displays a kit's metadata without knowing the kit grammar. All of them are display metadata with exactly the descriptor fields' authority: self-asserted, never trust inputs.
const ( DpkgStatusPath = "/var/lib/dpkg/status" ApkInstalledPath = "/lib/apk/db/installed" )
DpkgStatusPath and ApkInstalledPath are where the two package managers record what is installed. A kit's own filesystem is the only honest source for this: a descriptor states what its author knew, and what a base image actually shipped is knowable only by reading it.
const ( KindWorkload = "workload" KindMixin = "mixin" // KindSet is an authoring kind that never reaches a consumer: it // declares a kit whose content is the Kits list, and the frontend // publishes the merged result under the kind those kits imply — // workload when one of them is a workload, mixin when they all are. // Publishing "set" would oblige every consumer to learn a third kind // for a role that behaves exactly like one of the two that already // exist; deriving it also spares the author a claim about the set // that its kits already answer. Stating workload or mixin beside // Kits stays legal — it says the same thing explicitly, and the // frontend holds it to what the listed kits imply. KindSet = "set" )
Kit kinds. A workload's layers are a root filesystem that may stand at the bottom of the stack; a mixin's are an overlay that must land on one.
const ( // CapabilityNetworkPolicy is the phase-scoped egress policy; config // decodes to PhasedNetwork. CapabilityNetworkPolicy = "com.docker.sandbox/network-policy@1" // CapabilityNetworkPolicyV2 is the phase-scoped egress policy with // HTTP rules; config decodes to PhasedNetworkV2. A descriptor // declares one network-policy version, never both: the two describe // the same grant, and a host merging them would have to guess which // one bounds the other. CapabilityNetworkPolicyV2 = "com.docker.sandbox/network-policy@2" // CapabilityGitIdentity shares runtime-provided Git attribution, not credentials. CapabilityGitIdentity = "com.docker.sandbox/git-identity@1" // CapabilityCredential is one service the workload authenticates to; // config decodes to Credential. Instance-shaped, keyed by // (service, phase). CapabilityCredential = "com.docker.sandbox/credential@1" // CapabilitySSHAgent exposes an SSH agent to one phase, relaying to a // backing agent of the runtime's choosing; config decodes to SSHAgent. // Instance-shaped, keyed by phase. CapabilitySSHAgent = "com.docker.sandbox/ssh-agent@1" // CapabilityVolume is one persistent (or tmpfs) path; config decodes to // Volume. Instance-shaped, keyed by path. CapabilityVolume = "com.docker.sandbox/volume@1" // CapabilityHostMount shares a runtime-owned host directory across // sandboxes of one Kit. Its path key is shared with CapabilityVolume. CapabilityHostMount = "com.docker.sandbox/host-mount@1" // CapabilityPort is one in-container port to publish; config decodes to // Port. Instance-shaped, keyed by container port/transport. CapabilityPort = "com.docker.sandbox/port@1" // CapabilityAgentSkills declares one discovery path for bundled and // available host-shared skills; config decodes to AgentSkills. // Instance-shaped, keyed by path: a composition hosting two agents // that read skills from different places declares one entry each. CapabilityAgentSkills = "com.docker.sandbox/agent-skills@1" // CapabilityAgentSkill exposes one image-bundled skill, keyed by its effective name. CapabilityAgentSkill = "com.docker.sandbox/agent-skill@1" // CapabilityUSBDevice is one device passthrough request; config decodes // to USBDevice. Instance-shaped. CapabilityUSBDevice = "com.docker.sandbox/usb-device@1" // CapabilityResources constrains CPU, memory, and GPU; config decodes to // Resources. CapabilityResources = "com.docker.sandbox/resources@1" // CapabilityPrivileged requests elevated privilege; no config. Present or // absent — the host may refuse. CapabilityPrivileged = "com.docker.sandbox/privileged@1" // CapabilityLifecycle asks the engine to execute the kit's setup // across the sandbox's life: install hooks once at create, startup // hooks every boot, files written at start, and the interactive // argv tail for TTY sessions. A capability rather than // grammar fields so a host that cannot execute them refuses the kit // by name at preflight instead of silently never running its setup. // Like agent-sessions, not permission surface: hooks and files run // inside the sandbox on the entrypoint's trust plane. CapabilityLifecycle = "com.docker.sandbox/lifecycle@1" // CapabilityAgentContext asks the host to surface written instruction // content to the agent (the AGENTS.md family). A host with no such // concept skips an optional declaration or refuses a required one. CapabilityAgentContext = "com.docker.sandbox/agent-context@1" // CapabilityAgentSessions declares the workload's session control surface: // the argv shapes a harness uses to drive the agent — run one prompt // headlessly, list past sessions, resume or continue one. Not a // grant: the host consumes it to operate the agent, the way it // consumes the image config's entrypoint. Workload kits only in // practice — the agent the verbs drive is the workload's. CapabilityAgentSessions = "com.docker.sandbox/agent-sessions@1" // CapabilitySbx declares that a workload targets the sandbox agent // platform: the host launches the agent rather than letting the image // entrypoint be PID 1, honors the identity the image config states, // and provides the persistent-environment file the image names. The // kit's half is the floor that makes those duties performable — a // shell, bash, and a resolvable user — which the kit conformance // suite judges from the artifact. Not a grant: nothing crosses the // boundary the gate guards. No config; the identity lives in the // image config, where images already express it. CapabilitySbx = "com.docker.sandbox/sbx@1" // CapabilityLongRunning keeps a workload running independently of client // sessions. Workloads and mixins can request it; it takes no config. CapabilityLongRunning = "com.docker.sandbox/long-running@1" // CapabilityKitRegistry requests the runtime's kit registry — the daemon's // registry facade over the engine image store, where kit builds push // results and pull the kit frontend. Reaching it is the host's // answer (routing, address, transport), never an address the kit // guesses; the request is surfaced because image-store access is a // grant, not a side effect of network egress. No config. CapabilityKitRegistry = "com.docker.sandbox/kit-registry@1" )
Well-known capability types. Policy-shaped types appear at most once per descriptor; instance-shaped types (credential, volume, host-mount, port, usb-device, agent-skills) appear once per thing requested.
const ( SkillsReadOnly = "readonly" SkillsReadWrite = "readwrite" )
Skills access modes.
const ( SessionPromptPlaceholder = "{{.Prompt}}" SessionIDPlaceholder = "{{.SessionID}}" )
Placeholders the host substitutes into agent-sessions argv before execution. The {{.X}} shape matches the credential-file structure placeholders — one substitution vocabulary across the grammar.
const ( // SizeWarnBytes is the advisory budget: crossing it is a warning. SizeWarnBytes = 64 * 1024 // SizeErrorBytes is the hard budget: crossing it fails validation. SizeErrorBytes = 512 * 1024 )
Size limits for the published descriptor. OCI puts no limit on annotation values, but the manifest as a whole meets practical registry ceilings around 4 MB; the grammar exiles everything bulky, so a rich descriptor measures in single-digit kilobytes and these bounds exist so no kit ever discovers a registry's limit in production.
const ( DebNamespace = "deb" ApkNamespace = "apk" )
DebNamespace and ApkNamespace hold what a kit's own filesystem says it carries, rather than what its author wrote down: publishing reads the package databases and states one entry per installed package (§9.6).
Their own namespaces, and not com.docker.kit, because they are facts of a different kind and a different authority. A bare `node` is a claim an author makes about what the kit offers; `deb/nodejs-24` is what dpkg records, and the two must not collide in one namespace where a requirement could match either.
const AnnotationBuiltBy = "vnd.docker.sandbox.kit.built-by"
AnnotationBuiltBy names the frontend build that published the kit, as compact JSON decoding into BuiltBy. It identifies the tool that produced the artifact rather than the source the artifact was produced from — unlike revision, which §9.3 refuses. Absent on kits published before the annotation existed, so readers must tolerate it missing; and self-asserted, like every annotation here, so it answers "what claims to have built this", never "what is this allowed to do".
const AnnotationCapabilities = "vnd.docker.sandbox.kit.capabilities"
AnnotationCapabilities lists the requested capability types as a comma-separated string, deduplicated and sorted — e.g. "com.docker.sandbox/credential@1,com.docker.sandbox/network-policy@1". An index, not a source: the descriptor annotation stays authoritative, this exists so existence checks ("does this kit want privileged@1?") and policy filters read one small value without parsing the descriptor. Commas cannot appear in a type string, so splitting is unambiguous. Absent when the kit requests nothing.
const AnnotationDescriptor = "vnd.docker.sandbox.kit.descriptor"
AnnotationDescriptor is the OCI manifest annotation carrying the published descriptor as compact JSON. Authoring is YAML; the published form is a derived artifact (args expanded, contentFile rewritten), serialized as JSON to match the manifest it rides in. Consumers decode it with the same YAML decoder as authored files — YAML accepts JSON as a subset — so older kits whose annotations carry YAML keep decoding.
const AnnotationSchemaVersion = "vnd.docker.sandbox.kit.schema-version"
AnnotationSchemaVersion carries the descriptor's schemaVersion beside it, so tooling can dispatch on the grammar version from the manifest alone — no YAML parse — and a future schema bump is visible in a registry listing. Always equal to the schemaVersion field inside the descriptor.
const DefaultCapabilityNamespace = "com.docker.kit"
DefaultCapabilityNamespace qualifies bare capability names, the way docker.io/library qualifies bare image names: `gh` and `com.docker.kit/gh` are the same capability, while a third party's `com.example/gh` never matches either. Deliberately a sibling of com.docker.sandbox, not the same namespace — com.docker.sandbox names contracts the RUNTIME answers (capability types), com.docker.kit names vocabulary KITS provide.
const MethodAny = "ANY"
MethodAny is the NetworkEntry.Methods entry naming every method. It exists so a path-scoped entry can say "any method" explicitly rather than by omitting the field, which would leave the entry unbounded.
const SchemaVersion = "3"
SchemaVersion is the only schema version this package decodes.
Variables ¶
This section is empty.
Functions ¶
func AgentSkillName ¶
func AgentSkillName(s AgentSkill) string
AgentSkillName returns the directory component used at every destination.
func CanonicalRequire ¶
CanonicalRequire renders a parsed require in one spelling, so two entries meaning the same thing compare equal however they were written: "shell >= 1.0.0" and "shell>=1.0.0" and a bare name beside its namespace-qualified form are one relation, and the order two constraints were listed in is not part of what they say.
func CapabilityTypes ¶
func CapabilityTypes(capabilities []Capability) string
CapabilityTypes renders AnnotationCapabilities's value: the requested types, deduplicated, sorted, comma-joined. Sorted rather than authored order because the value is an existence index where order carries no meaning — sorting makes it canonical, so kits requesting the same types carry byte-identical values. Empty for an empty list, which callers translate to omitting the annotation.
func CheckReExport ¶
CheckReExport holds a set's arg declaration to the contract of the kit's arg it stands in for.
A re-export means the set's declaration is the only one an installer ever sees: the kit's is answered and gone. So the set's has to say at least as much — otherwise a required arg becomes optional and its reference goes unresolved at create, or an enum becomes anything at all and a value the kit would have refused reaches it with the kit's own declaration no longer there to refuse it.
Restating the constraint is the set author's work, and cheap: it is the one place an installer can read what the value has to be.
func CompareVersions ¶
CompareVersions compares dotted numeric versions, returning -1, 0, or 1. Non-numeric segments compare lexically, after numeric segments of equal position compare numerically — enough for release tags without importing a semver dependency.
func ContainsArgRef ¶
ContainsArgRef reports whether s references any kit arg.
func ContainsEnvRef ¶
ContainsEnvRef reports environment template syntax, including malformed references that must fail instead of becoming literal runtime policy.
func ContainsKitPlaceholder ¶
ContainsKitPlaceholder recognizes Kit template openers even when the name or closing braces are malformed. Inserted values must remain literal.
func DecodeCapabilityConfig ¶
func DecodeCapabilityConfig(n Capability, out any) error
DecodeCapabilityConfig decodes one entry's config into a well-known type's config struct, rejecting unknown fields.
func DerivedProvides ¶
DerivedProvides renders the provides entries a namespace's package listings agree on, one per package, sorted.
Every listing has to hold the package at one version. A kit publishes one descriptor for every platform it builds, so it may only state what is true of all of them: a package only some platforms carry, or one whose version moves between them, is something a single entry cannot say without lying about one of them. Those are dropped rather than refused, because an architecture-specific package is ordinary.
The same rule folds a multiarch database, where one name appears once per architecture within a single listing.
func DisplayCapabilityName ¶
DisplayCapabilityName renders a normalized name for humans: the default namespace is implied and stripped, anything else stays qualified — mirroring how image names display.
func EntryMethods ¶
func EntryMethods(e NetworkEntry) []string
EntryMethods renders an entry's methods as enforcement reads them: MethodAny when it named none, the stated tokens otherwise.
func EntryPaths ¶
func EntryPaths(e NetworkEntry) []string
EntryPaths renders an entry's paths as enforcement reads them: every path when it named none, the stated globs otherwise.
func ExpandBuildArgs ¶
ExpandBuildArgs expands build-phase references structurally, preserving the authored YAML's comments and ordering. Create-phase references remain.
func ExpandCreateArgs ¶
ExpandCreateArgs expands references to create-phase args (those NOT declaring buildArg:) through the descriptor, producing the EFFECTIVE descriptor: the document enforcement, the lock, and the permission gate judge. Expansion walks the decoded document — every string value and every map key — and re-serializes as JSON, so a value carrying quotes or backslashes survives instead of breaking the document it lands in. A string that is nothing but a reference adopts the value's own type, so a numeric arg can still land in a typed config field (`container: ${{ kit.args.port }}`) the way v2's substitution could. The published descriptor is never rewritten — callers expand a copy — and the declared enum/pattern constraints, which ride in the signed descriptor, bound what values can materialize. A reference with no resolved value is an error: a hole in effective policy must fail loudly, not enforce an empty string.
func ExpandString ¶
ExpandString substitutes ${{ kit.args.<name> }} references in s from values. Referencing a name absent from values is an error rather than an empty string, so a typo fails loudly instead of producing a hook that runs with a hole in it.
func FormatWidenings ¶
FormatWidenings renders a widening list for an approval prompt.
func HTTPMethods ¶
func HTTPMethods() []string
HTTPMethods returns the method tokens a NetworkEntry may name, excluding MethodAny, sorted. The published schema's enum is pinned against it.
func HasCapability ¶
func HasCapability(needs []Capability, typ string) bool
HasCapability reports whether a type is requested.
func HasEnvReferences ¶
func HasEnvReferences(items []Capability) bool
HasEnvReferences reports references in decoded configuration strings and keys, including group members. Checking serialized bytes would miss escaped whitespace.
func HasGroups ¶
func HasGroups(items []Capability) bool
HasGroups reports whether selection is needed before reconciliation.
func IsDerivedProvide ¶
IsDerivedProvide reports whether a normalized capability name is one publishing derived from image content rather than one an author wrote.
Three callers need the distinction, all of them because a derived entry is evidence and an authored one is a claim: the version annotation must not be drowned out by hundreds of package versions, the derivation must replace its own previous output rather than accumulate it, and a report reads better when it can say which is which.
func IsVersion ¶
IsVersion reports whether s is version-shaped by the one rule every consumer shares (tag ordering, provides matching, version inference).
func IsWholeArgRef ¶
IsWholeArgRef reports whether s is exactly one arg reference.
func KitArgValues ¶
KitArgValues resolves one listed kit's create-phase args from the values its set supplied, separating the two kinds of value a set may give.
A literal value is validated against the kit's own enum or pattern and expanded into the merged descriptor, which is what pins the set. A value that is itself a `${{ kit.args.* }}` reference cannot be validated here — it has no value yet — so it lands where the kit's placeholder was and the set's own declaration bounds it at create. That is the same two-phase judgment a parameterized capability entry gets, applied to a listed kit's inputs.
func KnownCapabilities ¶
func KnownCapabilities() []string
KnownCapabilities lists types understood by this version of the library. Understanding a schema is not a claim that a runtime implements it.
func LatestVersion ¶
LatestVersion returns the highest version-shaped tag (TagVersion rules, so a conventional "v" prefix counts), or "" when none parses as a version. The returned value is the tag as spelled — it names something pullable — while ordering compares the normalized versions. It is the one tag-ordering rule shared by the resolver ("unpinned means latest") and the gate ("moving from X to Y"), defined here so both agree on what "latest" means.
func NormalizeCapabilityName ¶
NormalizeCapabilityName qualifies a bare capability name with the default namespace; already-qualified names pass through. Matching, locks, and surfaces speak normalized names; authored short forms are display sugar.
func OCIAnnotations ¶
func OCIAnnotations(d *Descriptor) map[string]string
OCIAnnotations maps a published descriptor onto the standard org.opencontainers.image.* annotations it can populate: title, description, authors, source, and licenses straight from their fields, and version from the version: fallback or — when every versioned provides entry agrees — that one version. Empty fields yield no key, so absence means "not declared". Builder inputs (created, revision, base.*) are never emitted here: the descriptor cannot answer them. A builder may separately emit created from SOURCE_DATE_EPOCH per §9.3; this projection has no timestamp input and never reads the wall clock.
func OwnedProvides ¶
func OwnedProvides(contributions []Contribution) map[string][]OwnedProvide
OwnedProvides indexes what the contributions offer, by name, keeping the contribution each entry came from.
Exported alongside SatisfiedByOther because ownership is the whole of the rule: Resolve skips self, so anything judging a merge has to skip it the same way, and two implementations of that would drift.
func PackageVersion ¶
PackageVersion is the version a derived provide carries: the leading x.y.z core of what a package database records, with everything a distribution wrapped around it removed. Empty when the record holds no version this model can name, which drops the package rather than publishing it under the kit's own version.
What comes off is packaging bookkeeping rather than a point in the upstream order — a Debian revision, a binNMU, a backport suffix, an apk release — and a consumer writing `deb/openssl >= 3.5` cannot be asked to know about `-1~deb13u2+dhi1`. Fewer than three parts stays as it is and is never padded: binutils really is 2.44, and 2.44.0 would invent precision the distribution never stated.
func PortTransport ¶
PortTransport reports the transport a port request names, resolving the default. An omitted transport and an explicit "tcp" are the same request, so everything that identifies a port — the permission surface, the uniqueness check — has to see one spelling.
func Positions ¶
Positions maps every element of a descriptor document to its source location, keyed by dotted path with zero-based sequence indices — "capabilities[0].config.service", "args.version.pattern", "provides[2]". Consumers (the frontend) join these with FieldError.Path to point a validation error at the exact lines that caused it.
func PrivilegedOf ¶
func PrivilegedOf(needs []Capability) bool
PrivilegedOf reports whether the descriptor requests privileged execution.
func ProvidesIndex ¶
func ProvidesIndex(contributions []Contribution) map[string][]Provide
mergeRelations unions the kit-to-kit relations, dropping the requires and integrates entries the set answers itself.
Provides are facts about content that travelled in, so they carry over: the merged kit really does offer what its parts offered, and dropping them would let the same kit compose twice under a different reference. Requires are the opposite — an entry the set satisfies internally names something the merged kit now contains, and a kit cannot satisfy its own requirement (Resolve skips self), so keeping it would publish a kit that can never resolve. ProvidesIndex maps each capability name the contributions offer to every provide offering it, with each bare entry's version materialized from its own contribution's fallback.
A name maps to a list rather than one entry because the resolver counts OWNERS, not entries: two kits providing one name is refused, but a single kit may offer it at several versions, and whichever of them satisfied another kit's constraint during resolution has to still satisfy it here.
Exported because the set's own declarations are judged against the kits it lists before the merge runs, and both readings have to agree on what a bare provide's version is — the rule lives here so it cannot be restated differently in two places.
func ReferencedArgs ¶
ReferencedArgs returns the set of arg names referenced anywhere in the raw descriptor, for validation that every reference resolves.
func RequireAuthoredProvides ¶
func RequireAuthoredProvides(d *Descriptor) error
RequireAuthoredProvides refuses a provides entry an author must not write: §5.1 reserves the deb/ and apk/ namespaces for §9.6, where publishing fills them from the package databases in the content.
Authored form only, which is why it is not folded into Validate: the published form legitimately carries these, and a runtime revalidating a descriptor on load has to accept what publishing put there. The distinction is the whole point — an entry under these namespaces is something read off a filesystem, and one written by hand would assert a fact about content instead of offering a capability, with nothing left to catch the difference.
func RequireVersionedProvides ¶
func RequireVersionedProvides(d *Descriptor) error
RequireVersionedProvides is the publish-time rule the frontend enforces on the expanded descriptor: every provides entry must carry a version — its own @version, or the descriptor's version: fallback. A published kit with an unversioned provide would satisfy only unconstrained requires and silently defeat version-constraint resolution; the build is where the author can still fix it. Consumption references (a version tag) can override these versions, never substitute for them. Kits with no provides publish fine: they offer nothing matchable, so there is nothing to version.
func ResolveArgs ¶
ResolveArgs validates supplied values against the declarations and returns the effective value map: supplied values first, defaults for the rest. A required arg with no supplied value is an error; a supplied value failing its enum or pattern is an error; a supplied name the kit never declared is an error, because a silently ignored input is a misspelling in disguise.
func SatisfiedBy ¶
SatisfiedBy reports whether any provide under the require's name holds it. One kit may offer a name at several versions; the requirement is met if one of them meets it.
func SatisfiedByOther ¶
func SatisfiedByOther(owned map[string][]OwnedProvide, r Require, by string) bool
SatisfiedByOther reports whether a contribution other than by satisfies r. A contribution's own provide never does: Resolve skips self, so a requirement answered only by its own offer is one nothing can answer.
func Satisfies ¶
Satisfies reports whether the provide meets the require: same capability name, and a provided version that holds every stated constraint. An unversioned provide satisfies only an unconstrained require, so a constraint never silently matches a capability that declared no version.
func SkillsMode ¶
func SkillsMode(s AgentSkills) string
SkillsMode reports the access a request names, resolving the default.
func SkillsWritable ¶
func SkillsWritable(s AgentSkills) bool
SkillsWritable reports whether a request asks for write access.
Access is carried in a separate surface field rather than folded into the path string: a path is arbitrary text, so any suffix marking write could also be a real path, and a grant for one would silently cover a request for the other.
func TagVersion ¶
TagVersion maps a consumption-reference tag (an OCI tag, a git ref) onto the version it names: the bare version for a version-shaped tag, with git's conventional leading "v" tolerated and stripped; "" for everything else. The grammar itself admits no "v" — this is the one boundary where the outside world's spelling normalizes into the model's, so "v2.1.0" and "2.1.0" tags name the same version while only one spelling exists inside it.
func Validate ¶
func Validate(d *Descriptor) (warnings []string, err error)
Validate checks every rule the descriptor grammar states. It returns non-fatal warnings alongside all independent errors; a nil error with warnings means the descriptor is usable but the author should look. Errors are returned as ValidationErrors. Each FieldError carries the offending element's dotted path; WithSource adds source locations when the caller has the raw bytes.
func ValidateDeclarations ¶
func ValidateDeclarations(raw []byte, d *Descriptor) ([]string, error)
ValidateDeclarations validates all structure and per-entry configurations, leaving selection-dependent cross-entry checks to ValidateEffective.
func ValidateEffective ¶
func ValidateEffective(raw []byte, d *Descriptor) ([]string, error)
ValidateEffective is the create-time gate on the expanded descriptor: ValidateRaw with every capability entry validated strictly (no placeholder leniency can apply, because no placeholder may remain). The effective descriptor is the enforcement input, so a surviving reference would become literal policy.
func ValidateExpandedDeclarations ¶
func ValidateExpandedDeclarations(raw []byte, d *Descriptor) ([]string, error)
ValidateExpandedDeclarations additionally refuses unresolved arguments and environment references.
func ValidatePublished ¶
func ValidatePublished(raw []byte, d *Descriptor) (warnings []string, err error)
ValidatePublished runs ValidateRaw plus the published-form requirement: every build-phase reference has been expanded, so provides entries are literal. Create-phase references in hooks and files legitimately remain.
func ValidateRaw ¶
func ValidateRaw(raw []byte, d *Descriptor) (warnings []string, err error)
ValidateRaw runs Validate plus the checks that need the raw bytes: the size budget, and that every ${{ kit.args.* }} reference names a declared arg.
func WithSource ¶
WithSource renders every validation failure with a filename and, when available, a source position, source line, and caret. It preserves the original error tree for errors.Is/As. Callers without source bytes can use the field paths in Error() directly. Missing fields point at their containing element.
Example ¶
raw := []byte(`schemaVersion: "3"
kind: mixin
capabilities:
- type: com.docker.sandbox/port@1
config: {container: 99999}
args:
version:
pattern: '['
`)
d, err := Decode(raw)
if err != nil {
panic(err)
}
_, err = Validate(d)
fmt.Println(WithSource(err, "kit.yaml", raw))
Output: kit.yaml:5:25: capabilities[0].config.container: capabilities[0]: container port 99999 out of range | 5 | config: {container: 99999} | ^ kit.yaml:8:14: args.version.pattern: args.version: invalid pattern: error parsing regexp: missing closing ]: `[` | 8 | pattern: '[' | ^
Types ¶
type APIKey ¶
type APIKey struct {
// Name is the in-container env var, set to a sentinel when
// proxy-managed. Empty declares an inject-only credential: Inject
// rules are then required, and no variable is created for it.
Name string `json:"name" yaml:"name"`
// ProxyManaged keeps the real value on the host. A named key's
// variable carries a sentinel; an inject-only key (empty Name with
// Inject rules) has no in-container presence at all. Outbound, the
// boundary presents the real value either way.
ProxyManaged bool `json:"proxyManaged,omitempty" yaml:"proxyManaged,omitempty"`
// Inject lists the outbound domains the proxy rewrites.
Inject []Inject `json:"inject,omitempty" yaml:"inject,omitempty"`
}
APIKey configures proxy injection of an API key on outbound requests.
type AgentContext ¶
type AgentContext struct {
// Directory overrides workspace-sibling placement for agents whose
// startup discovery only reads a dedicated configuration directory.
Directory string `json:"directory,omitempty" yaml:"directory,omitempty"`
Filename string `json:"filename,omitempty" yaml:"filename,omitempty"`
// ContentFile points at the context body: a context-relative path in
// the authored descriptor, rewritten by the frontend to the staged
// in-image path in the published one.
ContentFile string `json:"contentFile,omitempty" yaml:"contentFile,omitempty"`
// Content carries small instruction content inline, for content-free
// kits.
Content string `json:"content,omitempty" yaml:"content,omitempty"`
}
AgentContext is CapabilityAgentContext's config: instruction content the agent reads. An explicit Directory and Filename select the agent's discovery profile; a workload Filename alone supplies the legacy fallback.
func AgentContextOf ¶
func AgentContextOf(capabilities []Capability) (*AgentContext, error)
AgentContextOf returns the agent-context declaration's config, or nil when none is declared.
func AgentContextsOf ¶
func AgentContextsOf(capabilities []Capability) ([]AgentContext, error)
AgentContextsOf reads all selected context contributions of one Kit. Unlike AgentContextOf (for an effective singleton), it retains separate bodies from selected groups for contributor-specific runtime guidance handlers.
type AgentSessions ¶
type AgentSessions struct {
Prompt []string `json:"prompt,omitempty" yaml:"prompt,omitempty"`
Resume []string `json:"resume,omitempty" yaml:"resume,omitempty"`
Continue []string `json:"continue,omitempty" yaml:"continue,omitempty"`
List CommandLine `json:"list,omitempty" yaml:"list,omitempty"`
}
AgentSessions is CapabilityAgentSessions's config. Prompt, Resume, and Continue are argv tails appended to the workload's launch command the way user-supplied args are: Prompt runs one prompt non-interactively and must reference {{.Prompt}}; Resume reopens a named session and must reference {{.SessionID}}; Continue reopens the most recent session. List is a complete command whose stdout enumerates resumable session ids, one per line, most recent first. Every verb is optional — an absent verb means the agent has no such operation — but a declaration with no verbs at all says nothing and is invalid.
func AgentSessionsOf ¶
func AgentSessionsOf(needs []Capability) (*AgentSessions, error)
AgentSessionsOf returns the agent-sessions declaration's config, or nil when none is declared.
type AgentSkill ¶
type AgentSkill struct {
Path string `yaml:"path" json:"path"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
}
AgentSkill identifies a complete skill directory in the published image. Name overrides its basename at discovery destinations, not in the source tree.
type AgentSkillCapability ¶
type AgentSkillCapability struct {
AgentSkill
Optional bool
DisplayName string
Description string
}
AgentSkillCapability exposes config.Name as Name and the entry label as DisplayName. Use AgentSkillName(request.AgentSkill) for the effective directory name, including the source-basename fallback when config.Name is omitted.
func AgentSkillRequestsOf ¶
func AgentSkillRequestsOf(capabilities []Capability) ([]AgentSkillCapability, error)
AgentSkillRequestsOf reads selected, flat declarations in contribution order. It preserves optionality so a runtime can refuse or record unavailable skills.
type AgentSkills ¶
type AgentSkills struct {
Path string `json:"path" yaml:"path"`
// Mode is "readonly" (default) or "readwrite".
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
}
AgentSkills declares Path as a destination for selected AgentSkill bundles and for the host's shared skills when available and enabled.
Only the kit knows where its agent looks: an agent shipped behind a wrapper, or one the runtime has never heard of, reads skills from a path no table on the host side can predict. Declaring the path is what makes the store reach kits the runtime does not recognize.
Mode is the most access the kit is willing to take, not a demand. The host's own setting narrows it further and can withhold the mount entirely without making the request unsatisfiable. Mode does not constrain bundled skills. Neither side can exceed the other, so a kit that only reads skills says so and never receives write access on a permissive host.
type AgentSkillsCapability ¶
type AgentSkillsCapability struct {
AgentSkills
Optional bool
Name string
Description string
}
AgentSkillsCapability is one skills request with the entry fields a runtime needs to answer it.
func AgentSkillsOf ¶
func AgentSkillsOf(needs []Capability) ([]AgentSkillsCapability, error)
AgentSkillsOf returns selected discovery destinations in declaration order. Every destination receives selected AgentSkill bundles. Optional governs capability support, not whether the host has skills available to share.
type Arg ¶
type Arg struct {
// Default is substituted when the caller supplies nothing. A nil
// default with Required unset still means the arg is optional and
// expands to the empty string only if referenced; declaring Required
// alongside Default is an error.
Default *string `json:"default,omitempty" yaml:"default,omitempty"`
// Required marks a value the caller must supply.
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
// Description is shown wherever a kit's inputs are listed.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// Enum restricts the value to an exact set. Exclusive with Pattern.
Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"`
// Pattern restricts the value to an RE2 regexp matched against the
// whole value. Exclusive with Enum.
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
// Env opts the resolved value into the container environment under
// this variable name.
Env string `json:"env,omitempty" yaml:"env,omitempty"`
// BuildArg resolves the arg at build instead: the frontend validates
// it, hands it to the companion dockerfile under this build-arg name,
// and expands its references into the published descriptor.
BuildArg string `json:"buildArg,omitempty" yaml:"buildArg,omitempty"`
}
Arg declares one installer-supplied value. Private by default: it reaches the running container only through Env, and the build only through BuildArg. The two are mutually exclusive because they resolve in different phases.
type BuiltBy ¶
type BuiltBy struct {
// Name is the frontend's published image, e.g. docker/sandbox-kit.
Name string `json:"name"`
// Version is the frontend's release, or "dev" for a build no release
// stamped.
Version string `json:"version"`
// Revision is the full commit the frontend was built from, omitted
// when the build recorded none.
Revision string `json:"revision,omitempty"`
}
BuiltBy is the value of AnnotationBuiltBy: which frontend build published a kit. JSON rather than one packed string because the fields are read apart — a version is compared, a revision is looked up — and because an object can gain a field without every reader learning a new way to split one.
func ParseBuiltBy ¶
ParseBuiltBy reads the annotation's value. The false report is for an absent annotation, which is the ordinary case for a kit published before the annotation existed, not a malformed one.
type Capability ¶
type Capability struct {
// Group is the alternative to an ordinary entry. It is not a capability
// type and must be selected before typed accessors are used.
Group *CapabilityGroup `json:"group,omitempty" yaml:"group,omitempty"`
// Source preserves publisher attribution. It is untrusted diagnostic
// metadata; the consuming artifact still determines trust.
Source *CapabilitySource `json:"source,omitempty" yaml:"source,omitempty"`
// Type names the capability and its config-schema version, e.g.
// "com.docker.sandbox/network-policy@1".
Type string `json:"type,omitempty" yaml:"type,omitempty"`
// Name is a display label, not identity: duplicate labels are allowed
// and renaming a request changes neither its grants nor its merge key.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Optional marks a capability the kit degrades gracefully without:
// an unknown or unprovided optional capability is skipped and recorded,
// while a required one fails resolution closed. For credentials this
// subsumes the older per-credential required flag.
Optional bool `json:"optional,omitempty" yaml:"optional,omitempty"`
// Config is the type-specific request payload. Well-known types are
// decoded strictly against the schema their version pins; unknown
// types carry it opaquely. Part of the permission surface: config
// changes gate like any widening.
Config map[string]any `json:"config,omitempty" yaml:"config,omitempty"`
// Description is shown wherever the request is listed or prompted.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// contains filtered or unexported fields
}
Capability is one typed, versioned capability request. The type string is <namespace>/<name>@<version>: the namespace admits host-specific capability types without touching this grammar, and the version moves when the type's config schema does — capabilities evolve without a descriptor schema-major bump.
func CapabilityWithConfig ¶
func CapabilityWithConfig(n Capability, config any) (*Capability, error)
CapabilityWithConfig returns the entry with its config replaced by the encoding of config, keeping everything that describes the request rather than stating it (type, optional, description). Callers that rewrite one well-known config — the frontend inlining a context body — go through this so the rewritten entry decodes exactly as an authored one would.
func DeclaredCapabilities ¶
func DeclaredCapabilities(items []Capability) []Capability
DeclaredCapabilities enumerates ordinary requests, including unselected members. Use only for artifact inspection, never runtime application.
func MapCapabilities ¶
func MapCapabilities(items []Capability, fn func(Capability) (Capability, error)) ([]Capability, error)
MapCapabilities visits ordinary entries, including group members, without selecting them. It copies slices and groups; fn must copy a config before changing it. Publishers use it to stage every potentially selected body.
func (Capability) ConfigStated ¶
func (c Capability) ConfigStated() bool
ConfigStated reports whether the document carried a `config` key, including the null and empty spellings the decoded map loses.
func (*Capability) UnmarshalJSON ¶
func (c *Capability) UnmarshalJSON(data []byte) error
UnmarshalJSON records config presence for the JSON spelling. Decoding is strict, as every other well-known config is — a custom unmarshaler does not inherit the caller's DisallowUnknownFields.
func (*Capability) UnmarshalYAML ¶
func (c *Capability) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML records whether the document stated a config at all, which the decoded map cannot distinguish from an omitted key. The callback form, not the node form: a node decodes through a fresh decoder that does not inherit KnownFields, which would silently accept misspelled capability fields.
type CapabilityDecision ¶
type CapabilityDecision struct {
Accepted bool `json:"accepted"`
Message string `json:"message,omitempty"`
}
CapabilityDecision is the runtime's answer for one expanded entry. Its zero value rejects the entry. Message explains acceptance or rejection for selection records and, on required rejection, error diagnostics.
type CapabilityGroup ¶
type CapabilityGroup struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Optional bool `json:"optional,omitempty" yaml:"optional,omitempty"`
Capabilities []Capability `json:"capabilities" yaml:"capabilities"`
}
CapabilityGroup selects one feature's requests atomically.
func (*CapabilityGroup) UnmarshalJSON ¶
func (g *CapabilityGroup) UnmarshalJSON(data []byte) error
func (*CapabilityGroup) UnmarshalYAML ¶
func (g *CapabilityGroup) UnmarshalYAML(unmarshal func(any) error) error
type CapabilityItem ¶
type CapabilityItem = Capability
CapabilityItem is the descriptor union: an ordinary Capability or Group. The alias keeps existing ordinary-entry Go literals source-compatible.
type CapabilitySource ¶
type CapabilitySource struct {
Kit string `json:"kit" yaml:"kit"`
Path string `json:"path" yaml:"path"`
}
CapabilitySource survives publication for diagnostics, not trust decisions.
func (*CapabilitySource) UnmarshalJSON ¶
func (s *CapabilitySource) UnmarshalJSON(data []byte) error
func (*CapabilitySource) UnmarshalYAML ¶
func (s *CapabilitySource) UnmarshalYAML(unmarshal func(any) error) error
type CommandLine ¶
type CommandLine []string
CommandLine accepts either a YAML string (run via "sh -c") or a list of argv elements.
func (*CommandLine) UnmarshalJSON ¶
func (c *CommandLine) UnmarshalJSON(data []byte) error
UnmarshalJSON mirrors the YAML flexibility on the JSON wire: lifecycle hooks live in a capability config, and configs reach their typed structs through a JSON round-trip (DecodeCapabilityConfig), so the string form must survive both decoders.
func (*CommandLine) UnmarshalYAML ¶
func (c *CommandLine) UnmarshalYAML(unmarshal func(any) error) error
UnmarshalYAML implements the string-or-list flexibility.
type Constraint ¶
type Constraint struct {
Op ConstraintOp
Version string
}
Constraint is one comparison in a require: Op Version.
type ConstraintOp ¶
type ConstraintOp string
ConstraintOp is a comparison against a version point.
const ( OpGTE ConstraintOp = ">=" OpGT ConstraintOp = ">" OpLTE ConstraintOp = "<=" OpLT ConstraintOp = "<" OpEQ ConstraintOp = "=" )
type ContextSource ¶
type ContextSource struct {
// Reference names the contributor, for the staged body's heading and
// for errors reading it.
Reference string
// Path is an in-image path to read the body from; empty when the
// body is inline.
Path string
// Content is the inline body; empty when Path is set.
Content string
// Target is set when publication preserves a separately selectable body.
Target string
}
ContextSource is one contribution's agent-context body: either an in-image path (a published kit's rewritten contentFile) or content stated inline.
type Contribution ¶
type Contribution struct {
// Reference is how the contribution is named in merge errors: a
// listed kit's consumption reference, or the set's own descriptor.
Reference string
// Descriptor is the declarations this contribution brings. A
// listed kit's is its published descriptor with its create-phase
// args already resolved (KitArgValues, ExpandCreateArgs). Publication
// preserves final-environment references; Compose requires those to be
// expanded before reconciliation.
Descriptor *Descriptor
}
Contribution is one descriptor folded into a merged kit, named by the reference its diagnostics should point at.
type Credential ¶
type Credential struct {
// Service is the identifier in the host credential store.
Service string `json:"service" yaml:"service"`
// Phase accepts one phase or a non-empty list of distinct phases.
Phase Phases `json:"phase" yaml:"phase"`
// APIKey configures header injection of an API key.
APIKey *APIKey `json:"apiKey,omitempty" yaml:"apiKey,omitempty"`
// OAuth configures proxy-managed OAuth token interception.
OAuth *OAuth `json:"oauth,omitempty" yaml:"oauth,omitempty"`
}
Credential is CapabilityCredential's config: one service the workload authenticates to and how the proxy presents proof — never where the secret lives. Phase-scoped like network: an install-only credential is injected only while install runs and is gone before the workload starts. An entry declaring both install and runtime uses the same configuration in both phases.
type CredentialCapability ¶
type CredentialCapability struct {
Credential
// Required mirrors the entry's optionality: a required credential
// fails preflight when no binding satisfies the service, an optional
// one is skipped and recorded.
Required bool
// Name and Description are the entry's display label and explanation.
Name string
Description string
}
CredentialCapability is one decoded credential request with its entry-level declaration state.
func CredentialsOf ¶
func CredentialsOf(needs []Capability) ([]CredentialCapability, error)
CredentialsOf returns every credential request, in declaration order.
func CredentialsOfPhase ¶
func CredentialsOfPhase(needs []Capability, phase string) ([]CredentialCapability, error)
CredentialsOfPhase returns the credential requests for one phase.
type CredentialFile ¶
type CredentialFile struct {
Path string `json:"path" yaml:"path"`
// Structure is a declarative nested map, encoded after placeholder
// substitution in the encoding Format selects, so its output is
// well-formed regardless of the values. Leaf strings may reference
// {{.AccessToken}} and {{.RefreshToken}} (strings), {{.ExpiresAt}}
// (a number), {{.Scopes}} (an array), and {{.PrimaryApiKey}} (a
// string whose enclosing key is omitted when no key is captured);
// each placeholder renders in the target encoding's own type.
// Unknown placeholders are errors.
Structure map[string]any `json:"structure,omitempty" yaml:"structure,omitempty"`
// Format selects the encoding of the substituted structure: "json"
// (the default when empty) or "toml", for agents that read TOML
// credential files (devin). Encoding stays declarative either way —
// nested maps become TOML tables, {{.ExpiresAt}} a TOML integer, and
// {{.Scopes}} a TOML array.
Format string `json:"format,omitempty" yaml:"format,omitempty"`
}
CredentialFile renders sentinels into a file the agent reads.
type Descriptor ¶
type Descriptor struct {
// SchemaVersion must be "3".
SchemaVersion string `json:"schemaVersion" yaml:"schemaVersion"`
// DisplayName is what humans read; identity lives in the reference.
DisplayName string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
// Author is who published the kit, in the freeform
// org.opencontainers.image.authors convention ("Name <email>", commas
// for multiples). Display metadata only, exactly like DisplayName: a
// self-asserted claim, never identity and never a trust input —
// publisher identity lives in the reference's registry namespace and
// in the signature.
Author string `json:"author,omitempty" yaml:"author,omitempty"`
// Description is a short human-readable summary.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// SourceURL points at the kit's source repository or documentation.
SourceURL string `json:"sourceUrl,omitempty" yaml:"sourceUrl,omitempty"`
// IconURL is an https URL to an image representing the kit in catalogs
// and pickers. Display metadata like DisplayName, and self-asserted
// the same way — but unlike the other display fields a consumer
// fetches and renders it, so the scheme is constrained rather than
// free text: https only keeps javascript:, data:, and file: out of
// whatever surface ends up rendering it.
//
// A URL rather than staged content because the surfaces that want an
// icon — a registry listing, a picker — have the manifest and not the
// layers. An icon in a layer could not be shown without pulling the
// kit, which is the moment the icon existed to inform.
IconURL string `json:"iconUrl,omitempty" yaml:"iconUrl,omitempty"`
// Version is the fallback version for unversioned provides entries,
// for kits consumed by references that carry no version of their own:
// a local directory, a git branch or commit. It never overrides the
// consumption reference — an OCI tag or a version-shaped git ref wins,
// so a stale Version cannot lie to the resolver — and an explicit
// provides@version outranks both. May reference a build-phase arg
// (`${{ kit.args.version }}`), expanded into the published descriptor
// like provides.
Version string `json:"version,omitempty" yaml:"version,omitempty"`
// Licenses lists SPDX license identifiers.
Licenses []string `json:"licenses,omitempty" yaml:"licenses,omitempty"`
// Kind is "workload" or "mixin". It names the role; the layer nature
// follows from it, and it tells the frontend what to emit.
Kind string `json:"kind" yaml:"kind"`
// Provides lists capabilities this kit offers, optionally versioned as
// name@version. Nothing is provided implicitly: a kit that wants to be
// requirable by name states so here.
Provides []string `json:"provides,omitempty" yaml:"provides,omitempty"`
// Requires lists capabilities that must be satisfied by the resolved
// set, as "name" or "name <op> version" (comma-AND'd constraints;
// ops: >=, >, <=, <, =). Capability names resolve to nothing and
// validate against something.
Requires []string `json:"requires,omitempty" yaml:"requires,omitempty"`
// Integrates lists capabilities the kit works with when present but
// functions without, in the requires grammar. An absent provider is
// fine; a PRESENT one that violates the stated constraints still fails
// resolution — an integration that would break is incoherence, not an
// option. A met entry orders like requires: the provider composes
// before the kit that integrates with it.
Integrates []string `json:"integrates,omitempty" yaml:"integrates,omitempty"`
// Conflicts lists capability names that must be absent from the
// resolved set.
Conflicts []string `json:"conflicts,omitempty" yaml:"conflicts,omitempty"`
// Capabilities declares everything the kit needs but cannot supply itself:
// a list of typed, versioned capability requests, each answered —
// granted, refused, or prompted — by the host. One model for every
// capability: well-known types get strict config schemas, cross-entry
// validation, and direction-aware gating; unknown types are the
// extension point, refused by name when required, skipped and
// recorded when optional. Everything the kit asks of its host lives
// here — resource grants and engine-executed behaviors alike
// (lifecycle hooks, agent context) — so a host answers the whole ask
// through one mechanism, or refuses the parts it does not know.
Capabilities []CapabilityItem `json:"capabilities,omitempty" yaml:"capabilities,omitempty"`
// Args declares installer-supplied values, keyed by arg name. Private
// by default: an arg reaches the container only via env:, and the
// build only via buildArg:.
Args map[string]Arg `json:"args,omitempty" yaml:"args,omitempty"`
// Build is the kit's content recipe inline: literal Dockerfile text,
// the single-file alternative to the companion <stem>.dockerfile
// (declaring both is an error). Full Dockerfile semantics apply —
// multi-stage, mounts, even a foreign # syntax= first line — because
// the frontend hands the block to the Dockerfile frontend verbatim;
// nothing is transpiled. Build-phase args reach it as --build-arg
// under their declared buildArg names, exactly like a companion.
// The published descriptor carries the block, so the annotation also
// records how the content was produced.
Build string `json:"build,omitempty" yaml:"build,omitempty"`
// Kits is this kit's content as a list of published kits rather than
// Dockerfile text: the fourth authoring form, for an environment
// assembled out of kits someone already published. Mutually
// exclusive with Build and Dockerfile — a kit's content recipe lives
// in exactly one place — and declared by kind: set, or beside the
// kind the listed kits imply.
//
// The frontend resolves every one of them, merges their layers and
// their declarations, and publishes one ordinary kit. The published
// descriptor retains the list with each entry pinned by Digest, the
// way it retains an inline Build block: the artifact still records
// how its content was produced.
Kits []Kit `json:"kits,omitempty" yaml:"kits,omitempty"`
// Dockerfile names the companion recipe explicitly, as a path
// relative to the descriptor's directory. Optional sugar over the
// filename-stem convention (<stem>.dockerfile beside the
// descriptor): stating it makes the pairing visible in the
// descriptor and frees the recipe's name from the stem. Unlike the
// conventional companion — whose absence just means a
// declaration-only kit — a named recipe that does not exist is an
// error: an explicit reference failing silently would build the
// wrong kit. Mutually exclusive with build:.
Dockerfile string `json:"dockerfile,omitempty" yaml:"dockerfile,omitempty"`
// contains filtered or unexported fields
}
Descriptor is the authored kit declaration. Published descriptors carry the same declarations with build-phase arg references (${{ kit.args.* }} for args with buildArg:) expanded, re-serialized as compact JSON.
func Compose ¶
func Compose(contributions []Contribution) (*Descriptor, error)
Compose reconciles ordered, expanded contributions for runtime use. It shares Merge's conflict and reconciliation rules, without staging content for publication. Retain the input contributions alongside the returned descriptor: capability handlers need the individual declarations wherever composition would otherwise erase their provenance. The caller resolves dependencies and validates inputs before calling; Compose does not perform descriptor validation or registry IO.
func Decode ¶
func Decode(data []byte) (*Descriptor, error)
Decode parses descriptor YAML strictly: unknown fields are errors, because a misspelled key silently ignored is a policy silently absent. A leading `# syntax=` line needs no special handling — it is a YAML comment.
func ExpandEnvironment ¶
func ExpandEnvironment(d *Descriptor, environment map[string]string) (*Descriptor, error)
ExpandEnvironment copies a descriptor and substitutes final-container environment values in capability configuration strings, including groups. It never reads the process environment or expands keys or descriptor metadata. Callers validate the result before selection and composition.
type FieldError ¶
type FieldError struct {
Path string
// contains filtered or unexported fields
}
FieldError is a validation failure attributed to one descriptor element. Path uses the same dotted form Positions produces, so an error and the source location it belongs to join without string parsing.
func (*FieldError) Error ¶
func (e *FieldError) Error() string
func (*FieldError) Unwrap ¶
func (e *FieldError) Unwrap() error
type File ¶
type File struct {
Path string `json:"path" yaml:"path"`
Content string `json:"content" yaml:"content"`
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
// Overwrite defaults to true; nil means unset. overwrite: false skips
// the write when the file already exists (e.g. on a persistent volume).
Overwrite *bool `json:"overwrite,omitempty" yaml:"overwrite,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
}
File is written at sandbox start because its content depends on runtime state the image cannot contain.
type HostMount ¶
type HostMount struct {
Path string `json:"path" yaml:"path"`
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
}
HostMount requests host-shared storage at an in-container path. The host location and provisioning mechanism belong to the runtime.
func HostMountsOf ¶
func HostMountsOf(needs []Capability) ([]HostMount, error)
HostMountsOf returns every selected host-directory request, in declaration order.
type Inject ¶
type Inject struct {
Domain string `json:"domain" yaml:"domain"`
Header string `json:"header,omitempty" yaml:"header,omitempty"`
// Format renders the credential into the header value, e.g. "Bearer %s".
Format string `json:"format,omitempty" yaml:"format,omitempty"`
// Scheme selects a non-header presentation, e.g. "basic".
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
// Username is the basic-auth username when Scheme is "basic".
Username string `json:"username,omitempty" yaml:"username,omitempty"`
}
Inject is one proxy header-injection rule.
type InstallHook ¶
type InstallHook struct {
Command CommandLine `json:"command" yaml:"command"`
User string `json:"user,omitempty" yaml:"user,omitempty"`
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
}
InstallHook runs once per sandbox at create. It sees only the environment it declares — deny by default — which is hygiene first and also makes its inputs enumerable.
type Kit ¶
type Kit struct {
// Ref is the reference this kit is consumed by, and the identity it
// is named by in every merge diagnostic. A registry reference: a
// local directory or a git URL names a kit that is not published
// yet, and a set's kits have to be resolvable from the manifest
// alone.
Ref string `json:"ref" yaml:"ref"`
// Digest pins this kit's manifest. Optional in the authored form —
// where a tag is how an author names a version — and required in
// the published one, which the frontend fills in from the reference
// it actually resolved.
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
// Args supplies this kit's create-phase args, keyed by its own arg
// names. A literal value is expanded into the merged descriptor at
// publish, which is what makes the set a pinned artifact. A value
// that is itself a `${{ kit.args.* }}` reference re-exports the
// input under the set's name: the reference lands where the
// placeholder was, and the set's own declaration — signed with the
// set — is what bounds the value an installer can supply.
Args map[string]string `json:"args,omitempty" yaml:"args,omitempty"`
}
Kit is one entry in a set's Kits list: a published kit the set is composed of.
It is named the way any kit is consumed — by a registry reference — because the set's whole content comes from it, and the frontend resolving that reference is what pins it. It carries no role: whether it lands as the base or as an overlay is its own descriptor's kind, which the set does not restate.
type Lifecycle ¶
type Lifecycle struct {
Install []InstallHook `json:"install,omitempty" yaml:"install,omitempty"`
Startup []StartupHook `json:"startup,omitempty" yaml:"startup,omitempty"`
Files []File `json:"files,omitempty" yaml:"files,omitempty"`
// Interactive is the argv tail appended to the workload's launch
// command for an interactive (TTY) session — the one piece of
// runtime config the image config has no native slot for (Cmd is
// single-valued). It lives in the lifecycle capability because the
// engine consumes it the way it consumes the hooks: behavior across
// the sandbox's life, not a grant.
Interactive []string `json:"interactive,omitempty" yaml:"interactive,omitempty"`
}
Lifecycle is CapabilityLifecycle's config: everything the engine executes on the kit's behalf across the sandbox's life. Install runs once per sandbox at create, in dependency order, with the network policy's install phase open; a hook sees only the env it declares. Startup runs every boot and must tolerate being re-run. Files are written at start rather than baked because their content depends on runtime state; ${{ kit.args.* }} expands at create, $VAR is left for the shell.
func ExpandLifecycle ¶
ExpandLifecycle applies arg expansion where the design permits it and nowhere else: install and startup commands, and files content. The capability list is never expanded — the permission gate judges literal policy — and the descriptor itself is never rewritten; the returned copy exists only for execution.
func LifecycleOf ¶
func LifecycleOf(capabilities []Capability) (*Lifecycle, error)
LifecycleOf returns the lifecycle declaration's config, or nil when none is declared.
type MergeOptions ¶
type MergeOptions struct {
// ContextPath is the in-image path the merged agent-context body
// will be staged at, written into the merged entry's contentFile.
// Required when any contribution declares context content.
ContextPath string
}
MergeOptions carries what the merge cannot derive from the contributions themselves.
type MergeResult ¶
type MergeResult struct {
// Descriptor is the merged declaration set, under the derived kind.
Descriptor *Descriptor
// ContextSources are agent-context bodies in contribution order. Empty
// when no contribution declared any context content. Sources with an
// empty Target are concatenated into the merged entry's contentFile.
// Each source with a Target must instead be staged separately at that
// path, preserving its conditional selection boundary. The caller owns
// reading and staging the bodies; this package only computes the paths.
ContextSources []ContextSource
}
MergeResult is the merged kit: the descriptor it publishes, plus the agent-context bodies the caller has to stage, which the descriptor alone cannot carry.
func Merge ¶
func Merge(contributions []Contribution, opts MergeOptions) (*MergeResult, error)
Merge folds ordered contributions into the one descriptor a flattened kit publishes.
The contributions arrive in composition order — providers before requirers, the set's own declarations last — because that order is what several of the rules below mean by "first" and "last", and deriving it needs the resolver, which does not belong here.
Unconditional context bodies are concatenated at opts.ContextPath; grouped publication stages each ContextSource at its separate Target. Runtime consumers use Compose and retain the input contributions.
type NetworkEntry ¶
type NetworkEntry struct {
// Hosts are the domain patterns this entry names. A bounded entry in
// an Allow list names them literally, because a pattern cannot be
// bounded and unbounded at once; Deny entries and unbounded entries
// may name patterns.
Hosts []string `json:"hosts" yaml:"hosts"`
// Methods are uppercase HTTP method tokens, or the single entry
// MethodAny. Stating any of them bounds the entry.
Methods []string `json:"methods,omitempty" yaml:"methods,omitempty"`
// Paths are globs matched against the request path. Stating one
// requires stating Methods, so a path always says which methods it
// bounds; omitted alongside methods, it means every path.
Paths []string `json:"paths,omitempty" yaml:"paths,omitempty"`
}
NetworkEntry is one egress grant: the hosts it names, and optionally the methods and paths it is bounded to.
A bare entry — hosts and nothing else — grants the connection, which is the whole of what @1 could say, and is written as a plain string. An entry stating methods or paths grants only matching HTTP requests; the connection carries no other traffic, so what the runtime cannot read as HTTP it refuses.
One list rather than a host list beside a separate HTTP block: the two would name the same host twice and leave the wider grant overriding the narrower, which is the opposite of what declaring the narrower means.
func (NetworkEntry) Bounded ¶
func (e NetworkEntry) Bounded() bool
Bounded reports whether the entry narrows its hosts to particular requests rather than granting the connection outright.
func (NetworkEntry) MarshalJSON ¶
func (e NetworkEntry) MarshalJSON() ([]byte, error)
MarshalJSON writes the shorthand back as a string, so a descriptor round-trips to what its author wrote instead of growing an object form they never used.
A stated-but-empty list is the one thing this type cannot write back: Bounded reads it as unbounded, the shorthand has nowhere to put it, and omitempty drops it from the object form. Either way the entry would come out wider than it went in, laundering something validation rejects into something it accepts, so it is refused rather than serialized.
func (*NetworkEntry) UnmarshalJSON ¶
func (e *NetworkEntry) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts a bare host string as shorthand for an unbounded entry, so a kit that bounds nothing writes the @1 list it always wrote. Object form decodes strictly, as every other well-known config does — a custom unmarshaler does not inherit the caller's DisallowUnknownFields.
type NetworkRules ¶
type NetworkRules struct {
Allow []string `json:"allow,omitempty" yaml:"allow,omitempty"`
Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
}
NetworkRules is an allow/deny pair of domain patterns. Deny wins.
type NetworkRulesV2 ¶
type NetworkRulesV2 struct {
Allow []NetworkEntry `json:"allow,omitempty" yaml:"allow,omitempty"`
Deny []NetworkEntry `json:"deny,omitempty" yaml:"deny,omitempty"`
}
NetworkRulesV2 is one phase's egress. Deny wins, as in @1.
type OAuth ¶
type OAuth struct {
TokenEndpoint *TokenEndpoint `json:"tokenEndpoint,omitempty" yaml:"tokenEndpoint,omitempty"`
ResourceHosts []string `json:"resourceHosts,omitempty" yaml:"resourceHosts,omitempty"`
Sentinels *Sentinels `json:"sentinels,omitempty" yaml:"sentinels,omitempty"`
CredentialFile *CredentialFile `json:"credentialFile,omitempty" yaml:"credentialFile,omitempty"`
ResponseFields *ResponseFields `json:"responseFields,omitempty" yaml:"responseFields,omitempty"`
// Passthrough returns the real token to the container: a downgrade.
Passthrough bool `json:"passthrough,omitempty" yaml:"passthrough,omitempty"`
}
OAuth configures the proxy's OAuth interception for one service.
type OwnedProvide ¶
OwnedProvide is a provide with the contribution that offered it, so a requirement answered by another kit can be told from one a contribution aims at itself.
type Package ¶
Package is one installed package as a database records it. Version is raw, exactly as written; PackageVersion is what turns it into something this model can name.
func ReadApkInstalled ¶
ReadApkInstalled lists the packages an apk installed database records.
apk keeps no removed-package records, so every stanza counts; the stanza shape is the difference, one letter per field.
func ReadDpkgStatus ¶
ReadDpkgStatus lists the packages a dpkg status file records as installed.
Only installed ones. A removed package keeps its stanza in that file with a config-files status, so taking every stanza would have a kit provide software it no longer carries — which is the opposite of what reading the filesystem was for.
type PackageDatabase ¶
type PackageDatabase struct {
Namespace string
Path string
Read func(io.Reader) ([]Package, error)
}
PackageDatabase is one package manager's record of what a filesystem carries, and the namespace its records are published under.
func PackageDatabases ¶
func PackageDatabases() []PackageDatabase
PackageDatabases are the databases §9.6 reads, in the order their entries are emitted.
One table, because two sides of publication consult it and a disagreement between them would be a conformance failure invented by the checker: the frontend derives entries from these paths, and the kit suite holds a published artifact's entries back to them.
type PhasedNetwork ¶
type PhasedNetwork struct {
Install *NetworkRules `json:"install,omitempty" yaml:"install,omitempty"`
Runtime *NetworkRules `json:"runtime,omitempty" yaml:"runtime,omitempty"`
}
PhasedNetwork is CapabilityNetworkPolicy's config: egress by phase — install is open while the install hooks run and closed before the agent starts; runtime is what the agent gets.
func NetworkPolicyOf ¶
func NetworkPolicyOf(needs []Capability) (*PhasedNetwork, error)
NetworkPolicyOf returns the network-policy@1 request's config, or nil when none is declared. A descriptor on @2 has no @1 entry and reads as nil here; callers that must see either version use NetworkPolicyV2Of.
type PhasedNetworkV2 ¶
type PhasedNetworkV2 struct {
Install *NetworkRulesV2 `json:"install,omitempty" yaml:"install,omitempty"`
Runtime *NetworkRulesV2 `json:"runtime,omitempty" yaml:"runtime,omitempty"`
}
PhasedNetworkV2 is CapabilityNetworkPolicyV2's config: PhasedNetwork's phases, each an allow/deny pair of entries rather than of bare hosts.
func NetworkPolicyV2Of ¶
func NetworkPolicyV2Of(needs []Capability) (*PhasedNetworkV2, error)
NetworkPolicyV2Of returns the network-policy request's config in the @2 shape whichever version declared it, or nil when neither is. An @1 config lifts into @2 as unbounded entries, which is what it means: every method and path on the hosts it allows. Consumers that read the policy to enforce it use this, so a kit does not change what they see by moving between versions.
type Phases ¶
type Phases []string
Phases accepts a scalar or list, preserving the scalar spelling when encoding a single-phase entry. Credentials and SSH agents share this grammar.
func (Phases) MarshalJSON ¶
func (*Phases) UnmarshalJSON ¶
type Port ¶
type Port struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Container int `json:"container" yaml:"container"`
Transport string `json:"transport,omitempty" yaml:"transport,omitempty"`
}
Port is CapabilityPort's config: one in-container port to publish.
func PortsOf ¶
func PortsOf(needs []Capability) ([]Port, error)
PortsOf returns every port request, in declaration order.
type Position ¶
Position is the 1-based source location of one YAML element.
func PositionFor ¶
PositionFor finds a field's location, falling back to its nearest containing element when a required field is absent. The empty path denotes the document, so even missing top-level fields have a location.
type Provide ¶
Provide is a parsed provides entry: a capability name with an optional version.
func ParseProvide ¶
ParseProvide parses "name" or "name@version". The parsed Name is normalized: bare names gain the default namespace.
type Require ¶
type Require struct {
Name string
Constraints []Constraint // empty when unconstrained
}
Require is a parsed requires/integrates entry: a capability name with zero or more version constraints. Empty Constraints means any provider of Name satisfies. Multiple constraints are ANDed (comma-separated in the authored form). Ranges and pins change only the Satisfies predicate: resolution stays a closed-set check, never a backtracking solver — the set already names its providers, and §5.3 admits at most one provider per capability name.
func ParseRequire ¶
ParseRequire parses a requires/integrates entry:
name name <op> version name <op> version, <op> version, ...
where <op> is one of >=, >, <=, <, =. Whitespace around operators and commas is optional. The legacy form "name >= version" is unchanged. Unsatisfiable constraint sets (conflicting pins, inverted bounds) are rejected at parse. The parsed Name is normalized: bare names gain the default namespace.
type Resources ¶
type Resources struct {
CPU float64 `json:"cpu,omitempty" yaml:"cpu,omitempty"`
Memory string `json:"memory,omitempty" yaml:"memory,omitempty"`
GPU string `json:"gpu,omitempty" yaml:"gpu,omitempty"`
}
Resources is CapabilityResources's config: the workload's compute.
func ResourcesOf ¶
func ResourcesOf(needs []Capability) (*Resources, error)
ResourcesOf returns the resources request's config, or nil when none is declared.
type ResponseFields ¶
type ResponseFields struct {
AccessToken string `json:"accessToken,omitempty" yaml:"accessToken,omitempty"`
RefreshToken string `json:"refreshToken,omitempty" yaml:"refreshToken,omitempty"`
ExpiresIn string `json:"expiresIn,omitempty" yaml:"expiresIn,omitempty"`
}
ResponseFields maps the provider's token-response field names. The refresh mapping is what lets a runtime mask the real refresh token when the provider spells the field nonstandardly (cursor's camelCase refreshToken) or reuses one field for both roles (devin's token).
type SSHAgent ¶
type SSHAgent struct {
Phase SSHAgentPhases `json:"phase" yaml:"phase"`
// Unrestricted defaults to true. False requires at least one bound.
Unrestricted *bool `json:"unrestricted,omitempty" yaml:"unrestricted,omitempty"`
Sign []string `json:"sign,omitempty" yaml:"sign,omitempty"`
Authenticate []string `json:"authenticate,omitempty" yaml:"authenticate,omitempty"`
}
SSHAgent is CapabilitySSHAgent's config. Phase accepts one phase or a non-empty list; each phase gets the same signing rules.
type SSHAgentCapability ¶
SSHAgentCapability is one SSH agent request with its entry-level optionality and display metadata, which the runtime needs for preflight.
func SSHAgentsOf ¶
func SSHAgentsOf(needs []Capability) ([]SSHAgentCapability, error)
SSHAgentsOf returns SSH agent requests in declaration order. One request may name both phases; callers enforcing one phase use SSHAgentsOfPhase.
func SSHAgentsOfPhase ¶
func SSHAgentsOfPhase(needs []Capability, phase string) ([]SSHAgentCapability, error)
SSHAgentsOfPhase returns requests granting the agent in a given phase.
type SSHAgentPhases ¶
type SSHAgentPhases = Phases
SSHAgentPhases is the phase list used by SSH-agent configurations.
type SelectCapability ¶
type SelectCapability func(context.Context, Descriptor, Capability) CapabilityDecision
SelectCapability answers whether the runtime will provide one expanded entry. The descriptor is the owning Kit after argument and environment expansion, including its DisplayName and all declarations before selection. Descriptor and capability inputs are passed by value. The context carries cancellation and deadlines. It must not apply the entry: the enclosing group can still be rejected.
func Supported ¶
func Supported(types ...string) SelectCapability
Supported constructs a selector from a runtime's claimed type list.
type Selection ¶
type Selection struct {
Capabilities []Capability `json:"capabilities"`
Selected []SelectionRecord `json:"selected,omitempty"`
Skipped []SelectionRecord `json:"skipped,omitempty"`
}
Selection retains the create-time decision separately from merged grants.
func SelectCapabilities ¶
func SelectCapabilities(ctx context.Context, d *Descriptor, selectCapability SelectCapability) (Selection, error)
SelectCapabilities validates declarations in their actual descriptor context before calling the selector and flattens only wholly selected constructs. A nil descriptor or selector is an error, never an implicit default. Required rejection returns records alongside the error for diagnostics. Cancellation returns a zero selection and the context error.
type SelectionRecord ¶
type SelectionRecord struct {
Path string `json:"path"`
Name string `json:"name,omitempty"`
Source *CapabilitySource `json:"source,omitempty"`
Members []string `json:"members"`
MemberSources []CapabilitySource `json:"memberSources,omitempty"`
Rejected []string `json:"rejected,omitempty"`
// Decisions follows Members order, including accepted members of skipped
// groups. Older persisted records may omit it.
Decisions []CapabilityDecision `json:"decisions,omitempty"`
}
SelectionRecord identifies an original item and its evaluated members. Path is in the consumed descriptor; Source, when present, is publisher metadata.
type Sentinels ¶
type Sentinels struct {
AccessToken string `json:"accessToken,omitempty" yaml:"accessToken,omitempty"`
RefreshToken string `json:"refreshToken,omitempty" yaml:"refreshToken,omitempty"`
}
Sentinels are the placeholder tokens rendered in-container.
type StartupHook ¶
type StartupHook struct {
Command CommandLine `json:"command" yaml:"command"`
User string `json:"user,omitempty" yaml:"user,omitempty"`
Background bool `json:"background,omitempty" yaml:"background,omitempty"`
Env []string `json:"env,omitempty" yaml:"env,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
}
StartupHook runs every boot and must tolerate being re-run.
type Surface ¶
type Surface struct {
NetworkInstallAllow []string `json:"networkInstallAllow,omitempty"`
NetworkInstallDeny []string `json:"networkInstallDeny,omitempty"`
NetworkRuntimeAllow []string `json:"networkRuntimeAllow,omitempty"`
NetworkRuntimeDeny []string `json:"networkRuntimeDeny,omitempty"`
// The HTTP lists are what may be done over the allowed connections,
// one atomic entry per method, host, and path as "<METHOD>
// <host><path>". A host with no allow rule bounding it appears under
// every method, because that is what it grants. Kept apart from the
// host lists because the two grant differently: a host list entry
// opens a connection, while these bound what may be done over it, so
// a gate naming both "network" could not say whether an update opened
// a new host or widened an existing one.
NetworkInstallHTTPAllow []string `json:"networkInstallHTTPAllow,omitempty"`
NetworkInstallHTTPDeny []string `json:"networkInstallHTTPDeny,omitempty"`
NetworkRuntimeHTTPAllow []string `json:"networkRuntimeHTTPAllow,omitempty"`
NetworkRuntimeHTTPDeny []string `json:"networkRuntimeHTTPDeny,omitempty"`
CredentialsInstall []string `json:"credentialsInstall,omitempty"`
CredentialsRuntime []string `json:"credentialsRuntime,omitempty"`
// SSHAgent holds what the SSH agent may sign, per phase: "<phase>"
// for an unbounded grant, "<phase> sign <namespace>" and "<phase>
// authenticate <[user@]host>" for a bounded one. Kept apart from
// credentials because the agent grants more than one service: an
// unbounded grant signs with every key it holds, for anything the
// sandbox asks.
SSHAgent []string `json:"sshAgent,omitempty"`
StoragePaths []string `json:"storagePaths,omitempty"`
// HostMountPaths grant access to host-shared directories. Keeping
// them apart from sandbox storage makes a change to host sharing a
// widening even when the in-container destination stays the same.
HostMountPaths []string `json:"hostMountPaths,omitempty"`
// SkillsPaths are the in-container paths the host's shared skills
// store is asked for. Kept apart from StoragePaths because the two
// grant different things: a volume is storage the sandbox is given,
// while this is a host directory the user filled with `sbx skills
// import` being handed to a kit. A gate naming both "storage" would
// not say which one was gained.
SkillsPaths []string `json:"skillsPaths,omitempty"`
// SkillsWritePaths are the subset of SkillsPaths the kit asks to
// write. Separate from the paths themselves so access cannot be
// confused with a path: write is the larger grant, and a sandbox that
// can rewrite the shared store affects every later one.
SkillsWritePaths []string `json:"skillsWritePaths,omitempty"`
Ports []string `json:"ports,omitempty"`
USB []string `json:"usb,omitempty"`
Privileged bool `json:"privileged,omitempty"`
// Services holds one entry per requested runtime-provided service:
// the type string, with a digest of the config when one is present,
// so a config change moves the surface the way any widening does.
// Optional requests are surfaced too — optional only changes what
// happens when the host cannot provide the service, not what is
// granted when it can.
Services []string `json:"services,omitempty"`
}
Surface is the permission-relevant projection of a descriptor: everything the host must grant, normalized (sorted, deduplicated) so two surfaces compare structurally. The gate stores a kit's surface in the lock and diffs it against a candidate update's; version movement that stays within the granted surface applies silently, movement that widens it stops for approval.
func SurfaceOf ¶
func SurfaceOf(d *Descriptor) Surface
SurfaceOf projects a descriptor's needs onto its permission surface. Supply the selected, merged descriptor for enforcement. On an unselected declaration this conservatively inventories all members, without group grants. Well-known types decode into the direction-aware fields the gate diffs with per-category semantics; every other type — and any known entry whose config fails to decode — lands in Services as type + config digest, so nothing a kit requests can escape the surface.
type TokenEndpoint ¶
type TokenEndpoint struct {
Host string `json:"host" yaml:"host"`
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}
TokenEndpoint names the OAuth token endpoint the proxy intercepts.
type USBDevice ¶
type USBDevice struct {
VendorID string `json:"vendorId,omitempty" yaml:"vendorId,omitempty"`
ProductID string `json:"productId,omitempty" yaml:"productId,omitempty"`
Class string `json:"class,omitempty" yaml:"class,omitempty"`
}
USBDevice is CapabilityUSBDevice's config: one passthrough match by vendor/product or class.
type USBDeviceCapability ¶
USBDeviceCapability is one decoded usb-device request with its entry-level declaration state.
func USBDevicesOf ¶
func USBDevicesOf(needs []Capability) ([]USBDeviceCapability, error)
USBDevicesOf returns every usb-device request, in declaration order.
type ValidationErrors ¶
type ValidationErrors []error
ValidationErrors contains independent failures in validation order. A validator returns nil on success, never an empty ValidationErrors. Each field failure remains accessible through errors.As and Unwrap.
func (ValidationErrors) Error ¶
func (e ValidationErrors) Error() string
func (ValidationErrors) Unwrap ¶
func (e ValidationErrors) Unwrap() []error
type Volume ¶
type Volume struct {
Path string `json:"path" yaml:"path"`
Size string `json:"size,omitempty" yaml:"size,omitempty"`
// Tmpfs makes the path a tmpfs mount instead of a block volume.
Tmpfs bool `json:"tmpfs,omitempty" yaml:"tmpfs,omitempty"`
// Mode is an octal permission string, e.g. "1777".
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
}
Volume is CapabilityVolume's config: one persistent (or tmpfs) path and its characteristics.
func VolumesOf ¶
func VolumesOf(needs []Capability) ([]Volume, error)
VolumesOf returns every volume request, in declaration order.
type Widening ¶
type Widening struct {
// Category names the permission class, e.g. "network.runtime.allow".
Category string `json:"category"`
// Detail is the specific grant being requested, human-readable.
Detail string `json:"detail"`
}
Widening is one way a candidate surface asks for more than a granted one.
func DiffWidenings ¶
DiffWidenings reports every way candidate exceeds granted. Removals and narrowings are not reported: giving up a permission needs no approval. A removed deny entry IS a widening — the deny was part of what made the grant acceptable.