Documentation
¶
Overview ¶
Governance profiles (migration 0052): the wire + store types for an ASSIGNABLE ceiling and the row that binds one to a subject.
Named `governance*` throughout since "profile" already means two other things in this tree (Recording-Mode synthesis, workspace scan profile), and grep ambiguity here is a containment-bug risk.
The rule for what a GROUP SUBJECT is, and what a USER SUBJECT is, in one place: internal/auth/oidc (the login-time match surface), internal/api (the write boundaries) and this package's own validators all call in here rather than restate it, so a change to identity normalisation is made once.
A group subject matches by exact equality against the printable-ASCII login-time snapshot, guarded BEFORE the Unicode fold; anything outside that set can never match anyone — a dead deny the console still renders active, or a phantom group tier.
Here rather than internal/auth/oidc because this package is the leaf every one of those surfaces already imports, while internal/auth/oidc carries the OIDC/JOSE stack that pkg/client and cmd/wardyn must not acquire to canonicalize a string. One owner, not one per dependency direction.
Package types defines Wardyn's core domain vocabulary — AgentRun, RunPolicy, CredentialGrant, ApprovalRequest, and the audit event shape — shared by the control plane, runners, sidecars, and the CRD layer.
User drives (migration 0054): the wire + store types for an admin-registered per-user storage allocation and the row that grants one to a subject.
Named `drive` throughout: a DRIVE is what an admin registers and allocates, MOUNT is what a run does with one. The member's run request carries only `{enabled, read_only}` and NEVER a path — the server derives the home name.
Two kinds fall out of the backend rather than being stored beside it: MANAGED (Wardyn allocates the object) and SHARE (the platform already has the tree and Wardyn binds one per-user subdirectory of it).
Everything here is a CLOSED enum validated at the write boundary, pinned by migration 0054's three CHECKs against internal/db's TestClosedEnumChecksMatchConstants.
Home-segment rules: what a per-person home name may look like per substrate (a Docker volume-name component; a Kubernetes DNS-1123 subdomain), and how each is worded to users.
Index ¶
- Constants
- Variables
- func ASCIIOnlySubject(s string) bool
- func BarrierLabel(c ConfinementClass) string
- func CLIGitHubCapabilities(caps []ghscope.Capability) []ghscope.Capability
- func CanonicalGroupSubject(s string) (string, bool)
- func CanonicalPushCollection(name string) (string, error)
- func CanonicalPushHost(provider, host string) (string, error)
- func CanonicalPushOrg(org string) (string, error)
- func CanonicalUserSubject(s string) string
- func ClosedADOTokenModeList() []string
- func ClosedCredentialSourceList() []string
- func ClosedGitLaneList() []string
- func ClosedGitProviderKindList() []string
- func ClosedIntegrationKindList() []string
- func ClosedModelProviderKindList() []string
- func ConfigKeyLooksSecret(key string) bool
- func DecodeComponentConfigSchema(data []byte) (ComponentConfigSchema, []ConfigIssue)
- func DeniesEveryPlacement(denied []Placement) bool
- func DenyPathSegments(pattern string) ([]string, error)
- func DriveGrantWidens(d UserDrive, prev *UserDriveGrant, g UserDriveGrant) string
- func DriveHomeName(d UserDrive, subject, override string) (string, error)
- func DriveHomeStricterRuleClause(b DriveBackend) string
- func DriveObjectID(id uuid.UUID) string
- func DriveObjectName(d UserDrive, home string) string
- func DriveObjectNamedByWardyn(b DriveBackend) bool
- func DriveSlug(name string) string
- func DriveSubjectHash(subject string) string
- func FoldWorkspaceContract(attachments []WorkspaceAttachment, sources map[uuid.UUID]Source, ...) map[string]WorkspaceRequirement
- func IsWildcardHost(host string) bool
- func ManagedBackendRejectsTemplate(backend DriveBackend, tmpl HomeTemplate) bool
- func PATRepoCovers(entry, key string) bool
- func PATRepoKey(forge, path string) (key string, ok bool)
- func PATRepoSet(entries ...string) *[]string
- func PrintableASCII(s string) bool
- func PushPathsDigest(paths []string) string
- func PushRuleEntryOfRemote(provider, remote string) (host, org string, err error)
- func PushRuleEntryOfRemoteAt(provider, remote, basePath string) (host, org string, err error)
- func PushRuleEntryOfServerRemote(provider, remote, basePath string) (host, org string, err error)
- func PushRuleKey(provider, host, org string) (string, error)
- func RemoteRunnerDefaultName(id string) string
- func ShareBackendRejectsTemplate(backend DriveBackend, tmpl HomeTemplate) bool
- func SplitRequirementKey(key string) (typ, rest string, ok bool)
- func SuggestedRunnerMaxRuns(cpus int) int
- func ValidComponentName(name string) error
- func ValidInjectionFormat(format string) error
- func ValidMachineName(s string) bool
- func ValidRemoteRunnerID(id string) bool
- func ValidRunnerMaxRuns(n int) bool
- func ValidToolEffect(e ToolEffect) bool
- func ValidateComponentRefs(refs []ComponentRef, validHost func(string) error) error
- func ValidateDriveName(name string) error
- func ValidateHarnessCredentials(creds []HarnessCredential) error
- func ValidateHarnessToolRules(sets []HarnessToolRules) error
- func ValidatePushRuleSets(sets []PushRuleSet) error
- func ValidateRemoteRunnerName(name string) error
- func ValidateRunnerPoolName(name string) error
- func ValidateRunnerPoolUseSubjects(subjects []RunnerPoolSubject) error
- func ValidateUserDrive(d *UserDrive, runnerTarget string) error
- func ValidateUserDriveGrant(g *UserDriveGrant) error
- type ADOEntraConfig
- type ADOTokenMode
- type APIToken
- type ActorType
- type AgentProvider
- type AgentProviders
- type AgentRun
- type ApprovalDecision
- type ApprovalEscalation
- type ApprovalKind
- type ApprovalNotifyChannelStat
- type ApprovalNotifyState
- type ApprovalRequest
- type ApprovalScope
- type ApprovalState
- type ArtifactOverridedeprecated
- type AttentionBy
- type AttentionKind
- type AuditEvent
- type AuditRetentionDrop
- type AuditRetentionPartition
- type AuditRetentionPolicy
- type AuditRetentionPolicyChange
- type AuditRetentionStatus
- type AutonomyEgressPosture
- type AutonomyLevel
- type AutonomyPosture
- type AutonomyResolution
- type AutonomyRubric
- type AutonomySecretsPosture
- type AzureSettings
- type BaseImageEntry
- type BedrockSettings
- type Branding
- type CLIAWS
- type CLIAzure
- type CLIGitHub
- type CapabilityEffect
- type CapabilityGrant
- type CapabilitySubjectType
- type CeilingOverlay
- type Component
- type ComponentCLI
- type ComponentConfigSchema
- type ComponentConfigValues
- type ComponentDefinition
- type ComponentDelivery
- type ComponentKind
- type ComponentMCP
- type ComponentRef
- type ComponentSecret
- type ComponentSettings
- type ConcurrencyBound
- type ConfigBindTarget
- type ConfigBinding
- type ConfigCondition
- type ConfigField
- type ConfigFieldKind
- type ConfigGroup
- type ConfigIssue
- type ConfigOption
- type ConfinementClass
- type CredentialDeliveryPolicyDoc
- type CredentialGrant
- type CredentialSource
- type Delegate
- type DelegatedToken
- type DelegationVia
- type Destination
- type Device
- type DeviceAck
- type DeviceEnrolRequest
- type DeviceEnrolResponse
- type DeviceEnrolmentToken
- type DriveBackend
- type DriveKind
- type DriveMount
- type DriveObjectScheme
- type DriveReclaim
- type EffectiveLimit
- type EffectiveRunLimits
- type EgressRedirect
- type EphemeralProvider
- type FederatedAuditEvent
- type FirstUseMode
- type GitLane
- type GitPATScope
- type GitProvider
- type GitProviderKind
- type GovernanceAssignment
- type GovernanceChange
- type GovernanceLimits
- type GovernanceProfile
- type GrantDelivery
- type GrantKind
- type GrantSpec
- type HarnessCredential
- type HarnessToolRules
- type HomeTemplate
- type ImageBuild
- type ImageBuildFeature
- type ImageBuildGenerator
- type ImageBuildImage
- type ImageBuildInputs
- type ImageBuildInstaller
- type ImageBuildRef
- type ImageBuildRepo
- type Integration
- type IntegrationDelivery
- type IntegrationList
- type IntegrationSecret
- type InternalHost
- type Invitation
- type InvitationState
- type LLMInspectionSpec
- type LaunchPreset
- type LimitAmount
- type LimitSource
- type LimitsOverlay
- type LostReason
- type MemberDrivePolicy
- type ModelProvider
- type ModelProviderKind
- type ModelProviders
- type ModelProvidersRead
- type OverrideDirection
- type OverrideKind
- type OverrideNarrowing
- type OverrideOp
- type OverrideRule
- type PauseReason
- type Person
- type PersonList
- type PersonSummary
- type Placement
- type ProviderAuth
- type ProviderHarness
- type PushContentScope
- type PushPathList
- type PushRefUpdate
- type PushRuleSet
- type PushRulesOverlay
- type PushRulesSpec
- func PushRulesForRemote(sets []PushRuleSet, provider, remote string) (spec PushRulesSpec, found bool, err error)
- func PushRulesForRemoteAt(sets []PushRuleSet, provider, remote, basePath string) (spec PushRulesSpec, found bool, err error)
- func PushRulesForServerRemote(sets []PushRuleSet, provider, remote, basePath string) (spec PushRulesSpec, found bool, err error)
- type RemoteRunner
- type RemoteRunnerPeoplePools
- type RemoteRunnerPoolRef
- type ResolvedDrive
- type ResolvedInjection
- type ResolvedRunnerPool
- type ResourceLimits
- type ResourcesOverlay
- type RoleMapping
- type RunAttention
- type RunComponent
- type RunEndReason
- type RunEvidenceSource
- type RunExperience
- type RunHarness
- type RunLayout
- type RunLayoutWidget
- type RunLimits
- type RunPolicy
- type RunPolicySpec
- type RunQueue
- type RunState
- type RunTemplate
- type Runner
- type RunnerActionKind
- type RunnerAmounts
- type RunnerCapacity
- type RunnerClaimRequest
- type RunnerFilter
- type RunnerKind
- type RunnerLimits
- type RunnerMutationFailure
- type RunnerPendingAction
- type RunnerPool
- type RunnerPoolAmount
- type RunnerPoolBackgroundLimits
- type RunnerPoolDefaults
- type RunnerPoolDuration
- type RunnerPoolInteractiveLimits
- type RunnerPoolLimits
- type RunnerPoolMember
- type RunnerPoolRef
- type RunnerPoolRunType
- type RunnerPoolScope
- type RunnerPoolSelection
- type RunnerPoolState
- type RunnerPoolSubject
- type RunnerPoolUsePolicy
- type RunnerPosture
- type RunnerQuarantine
- type RunnerRegisterRequest
- type RunnerRegistration
- type RunnerRegistrationToken
- type RunnerRetirement
- type RunnerSettings
- type RunnerSettingsRequest
- type RunnerSpoolReceipt
- type RunnerState
- type RunnerTally
- type RunnerTokenRequest
- type RunnerUpdateRequest
- type RunnerUsage
- type RunnerView
- type SSHPublicKey
- type ServiceIdentity
- type ServiceIdentityState
- type SiteBranding
- type SiteConfig
- type SiteEgress
- type Source
- type SourceKind
- type StorageEnforcement
- type StorageProviders
- type TemplateAction
- type TemplateActor
- type TemplateDenial
- type TemplateGroupAdmin
- type TemplateKind
- type TemplateOwner
- type TemplateRoute
- type TemplateScope
- type TemplateTier
- type TokenDisposition
- type TokenMove
- type TokenMoved
- type ToolEffect
- type ToolRule
- type UIApp
- type UserDrive
- type UserDriveGrant
- type UserDriveHostRootCheck
- type UserDriveListItem
- type UserDriveProvider
- type UserType
- type WithheldScmHost
- type Workspace
- type WorkspaceAttachment
- type WorkspaceBaseImage
- type WorkspaceKind
- type WorkspaceLLMCred
- type WorkspaceMount
- type WorkspaceProviders
- type WorkspaceRepo
- type WorkspaceRequirement
- type WorkspaceSource
- type WorkspaceSourceType
- type WorkspaceStatus
Constants ¶
const ( BrandNamePrefix = "prefix" BrandNameSuffix = "suffix" )
Branding name formats: "<Company> Wardyn" and "Wardyn for <Company>".
const ( // ComponentDeliveryHeader: the proxy presents the secret in a request // header; the sandbox never holds it. ComponentDeliveryHeader = "header" // ComponentDeliveryEnv and ComponentDeliveryFile put the secret inside // the sandbox for the whole run. ComponentDeliveryEnv = "env" ComponentDeliveryFile = "file" )
How one component secret reaches the run.
const ( MaxComponentHosts = 32 MaxComponentSecrets = 8 MaxComponentConfigKeys = 32 MaxComponentConfigValueBytes = 4096 MaxComponentNameRunes = 64 MaxComponentRefs = 16 // MaxComponentEnvNameBytes bounds an env var or config key name; // MaxComponentHeaderFormatBytes the header value template, which the // proxy writes onto every injected request. MaxComponentEnvNameBytes = 128 MaxComponentHeaderFormatBytes = 512 )
Bounds on one definition and on one run's attachments.
const ( ComponentBuiltinGitHub = "github" ComponentBuiltinAzureDevOps = "azure_devops" )
The built-in Git provider components a ComponentRef may name.
const ( ComponentAccessRead = "read" ComponentAccessWrite = "write" )
Built-in component access levels.
const ( MaxComponentBuiltinRepos = 64 MaxComponentBuiltinFieldBytes = 256 )
MaxComponentBuiltinRepos bounds the repositories one built-in component lists.
const ( CLIKindAzure = "azure" CLIKindAWS = "aws" CLIKindGitHub = "github" )
CLI kinds a component row can carry. gcloud and kubectl are not built.
const ( MaxComponentMCPURLBytes = 2048 MaxComponentMCPHarnesses = 8 )
Bounds on an MCP component and on what one reference turns it on for.
const ( MaxConfigSchemaBytes = 64 << 10 MaxConfigSchemaFields = 64 MaxConfigSchemaGroups = 16 MaxConfigOptions = 64 MaxConfigListItems = 32 MaxConfigLabelRunes = 80 MaxConfigDescriptionRunes = 500 MaxConfigOptionValueRunes = 128 )
Bounds on a schema and its values.
const ( PATAccessRead = "read" PATAccessWrite = "write" PATForgeGeneric = "generic" PATForgeGitLab = "gitlab" PATForgeBitbucketServer = "bitbucket_server" PATForgeGitea = "gitea" )
Values of a git_pat scope's access and forge axes.
const ( GovernanceChangePending = "pending" GovernanceChangeApplied = "applied" GovernanceChangeRejected = "rejected" GovernanceChangeExpired = "expired" GovernanceChangeStale = "stale" )
Governance change states (migration 0126's CHECK).
const ( GovernanceTargetProfile = "governance_profile" GovernanceTargetAssignment = "governance_assignment" GovernanceTargetCapabilityGrant = "capability_grant" GovernanceTargetCapabilityEnforcement = "capability_enforcement" GovernanceTargetCapabilityAvailability = "capability_availability" GovernanceTargetUserTypePriority = "user_type_priority" GovernanceTargetRoleMapping = "role_mapping" GovernanceTargetKeyDomainAssignment = "key_domain_assignment" GovernanceTargetEgressBaseline = "egress_baseline" GovernanceTargetRunnerPoolUsePolicy = "runner_pool_use_policy" GovernanceTargetRemoteRunnerPeople = "remote_runner_people_policy" GovernanceTargetCredentialDelivery = "local_credential_delivery" GovernanceTargetServiceIdentity = "service_identity" )
The target kinds a governance change can hold (governance_changes.target_kind has no CHECK: the closed set is the table in internal/api, and a later lane adds a kind with no DDL).
const ( // HoldWindowEgress is how long an egress_domain wait_for_review request // holds the sandbox from when it was raised (proxy's defaultHoldTimeout). // This under-reports a policy authored above 30s (first_use_hold_seconds, // up to maxHoldTimeout) — ported verbatim from the TS rule it replaces; // fixing the under-report is a separate proxy change (the proxy would // need to stamp its own resolved hold_seconds into the raise scope), // not this one. HoldWindowEgress = 30 * time.Second // HoldWindowADOCapability is how long an Azure DevOps capability // escalation (a tool_call with grant_id set) holds the sandbox (proxy's // maxCapabilityHoldTimeout). HoldWindowADOCapability = 240 * time.Second // HoldWindowPush is the proxy's absolute ceiling on a push_content hold // (maxHoldTimeout) — the conservative bound isHeld used when no client // surface reads a run's own resolved push_rules.hold_seconds. HoldWindowPush = 600 * time.Second )
The three hold windows approval.Hold ports from ui/src/app/lib/types/ approvals.ts's now-retired isHeld — see that function's own history (#725/ F1, #509, #181) for why each window is what it is. Exported here, the one package both internal/approval and internal/egress/proxy already import, so the proxy's own defaultHoldTimeout/maxHoldTimeout/maxCapabilityHoldTimeout point at these instead of carrying a second copy of the same numbers (#1197).
const ( ImageBuildLaneGenerated = "generated" ImageBuildLaneBYOI = "byoi" ImageBuildLaneRepo = "repo_devcontainer" ImageBuildReasonCacheMiss = "cache_miss" ImageBuildReasonImageMissing = "image_missing" ImageBuildReasonRefresh = "refresh" )
Build lanes and the reasons a build ran, as the record names them.
const ( // MintConflictPending — the approval is still open; approval_id is set and // the caller may poll. MintConflictPending = "pending" // MintConflictDenied — a human denied the approval; denied/reason are set. MintConflictDenied = "denied" // MintConflictScopeMismatch — the requested scope widens the grant. MintConflictScopeMismatch = "scope_mismatch" // MintConflictAlreadyMinted — the grant's single-use slot is already spent. MintConflictAlreadyMinted = "already_minted" )
Mint 409-conflict "code" values: the machine-readable discriminator between the four distinct fail-closed conditions that all share HTTP 409 on POST /wardyn/v1/credentials/mint. THIS IS THE ONE HOME: internal/api's mint handler and cmd/wardyn-git-helper's client both read these, and internal/api's tests assert the LITERAL strings (a wire contract).
const ( AzureRouteAnthropic = "anthropic" AzureRouteOpenAIV1 = "openai_v1" )
The two inference routes an azure_foundry row serves. One row serves one route, and a route names the one Entra audience its sign-in is captured for.
const ( PushPathListMaxPaths = 10_000 PushPathListMaxBytes = 1 << 20 )
The bounds of a PushPathList: at most PushPathListMaxPaths paths and PushPathListMaxBytes bytes of path text, whichever cuts the list first.
const ( PushActsAsGitHubApp = "github_app" PushActsAsGitPAT = "git_pat" PushActsAsADOEntra = "ado_entra" )
The PushContentScope.ActsAsKind values: which lane's credential a held push authenticates with, so the console can word the card.
const ( PublicGitHubHost = "github.com" PublicAzureDevOpsHost = "dev.azure.com" )
The public host of each provider, which an authored set or override that names no host is for.
const ( PostureEvidenceMDMFile = "mdm_file" PostureEvidenceOSProbe = "os_probe" )
RunnerPosture.Evidence values.
const ( RunnerActionSuccess = "success" RunnerActionDeferred = "deferred" // RunnerActionRefused closes an action the runner can never apply. RunnerActionRefused = "refused" )
Runner action outcomes an action_result may carry. A deferred receipt is final for its action ID but does not settle the obligation it carried.
const ( // RunnerMaxRunsCeiling bounds a self-hosted runner's Runs at once (migration 0172's check). RunnerMaxRunsCeiling = 64 // RemoteRunnerMaxRunsCeiling bounds a remote-hosted runner's Runs at once (migration 0174's check). RemoteRunnerMaxRunsCeiling = 1024 // RunnerCPUMillisMin is the least CPU a runner's total or per-run limit may name: one CPU. RunnerCPUMillisMin = 1000 // RunnerDiskStepMiB: a runner's disk limits are whole GiB, as a run's main drive is sized. RunnerDiskStepMiB = 1024 // RunnerMaxRunsDefault is the max_runs of a runner registered before 0172. RunnerMaxRunsDefault = 2 )
const ( RunnerPoolCPUMillisMax = 1_000_000 // milli-CPU: a thousand CPUs RunnerPoolMemoryMiBMax = 16 << 20 // MiB: 16 TiB RunnerPoolDiskMiBMax = 16 << 20 // MiB: 16 TiB, the request contract's disk bound RunnerPoolSecondsMax = math.MaxInt32 // about 68 years )
The bounds every pool amount and duration is validated against. They are the run request's own bounds (CPU and memory) and the run limits' (seconds), so a pool can never name a value the request contract refuses.
const ( // The four AI kinds (anthropic_api_key, anthropic_subscription, bedrock, // openai_api_key) are GONE as of 0.8: model access is a model provider, and // migration 0100 converted and deleted every stored row of them. IntegrationKindGitHubApp = "github_app" IntegrationKindGitHost = "git_host" )
Integration kinds. An integration is a BASE COMPONENT extended by kind: a connection (secrets + egress), never an installer. The closed set below is every kind with bespoke behavior; any other kind is a GENERIC open slug carrying its own secrets/egress/config. UI grouping DERIVES from kind; there is no stored category.
const ( // DeliveryProxyHeader: the proxy presents the secret in an HTTP header; // the sandbox never holds it. The default, never-resident lane. DeliveryProxyHeader = "proxy_header" // DeliveryResidentFile/DeliveryResidentEnv: materialized as a file, or // exported as an env var, inside the sandbox — disclosed exceptions. DeliveryResidentFile = "resident_file" DeliveryResidentEnv = "resident_env" )
Integration delivery modes: how one secret reaches the run.
const ( OverrideOff = "off" // this workspace refuses the requirement outright OverrideOptional = "optional" // re-lane: off by default, a run may enable it OverrideRequired = "required" // re-lane: rides every run )
AttachmentOverride values: a workspace's per-attachment stance on one requirement key its source declares.
const ( ADOPATMaxHoursDefault = 8 // minted_pat: PATMaxHours 0 reads as this ADOPATMaxHoursLimit = 168 // minted_pat: the longest a token may live, in hours ADOPATMaxDaysDefault = 30 // own_pat: PATMaxDays 0 reads as this ADOPATMaxDaysLimit = 90 // own_pat: the longest a pasted token may live, in days )
Bounds and defaults for the personal-access-token lifetimes on ADOEntraConfig.
const ADOEntraAccessTokenSecret = "azure-devops-entra-access-token"
ADOEntraAccessTokenSecret is a SENTINEL secret name (NOT a stored secret) that resolves to an Azure DevOps access token minted from the RUN OWNER's own captured Entra sign-in (internal/api's resolveADOInjection). Nothing is stored under this name: the credential in the store is the person's refresh token, held in the reserved `wardyn-harness-ado-<row>-oauth` blob, and the access token exists only for the moment it is handed to the run's proxy sidecar.
SECURITY: THE TOKEN DOES NOT BOUND THE RUN, and no reader of this name may assume it does — measured against a real tenant, Entra issues an Azure DevOps token carrying EVERY scope the person consented to regardless of what subset is requested, so what holds a run to its granted capabilities is Wardyn's own capability check in front of the resource (the proxy), not the credential. The granted scope string the authority reports is recorded on the audit row because a token is opaque and that string is the only honest evidence of what the credential can do.
Deliberately NOT in sinkReservedSecret, for AWSSSOAccessTokenSecret's reason: being resolved at that sink, host-pinned, is the whole point.
const AWSSSOAccessTokenSecret = "aws-sso-access-token"
AWSSSOAccessTokenSecret is a SENTINEL secret name (NOT a stored secret) that works like the two OAuth sentinels above at the injection sink (host-pinned, forced header, masked), resolving to the run's OWN captured AWS IAM Identity Center access token — the credential an `aws sso login` captured into wardynd's secret store as a whole blob, not as a value under this name.
This is what makes portal.sso.<region> GetRoleCredentials (`authtype:none`) work: the proxy carries the session as the x-amz-sso_bearer_token HEADER and the sandbox never holds the token. The resolver, internal/api's resolveAWSSSOInjection, also re-derives the credential's scope from the roster and refuses any drift from the snapshot the grant was authored with.
Deliberately NOT in sinkReservedSecret: that guard refuses names an api_key grant must never resolve, but this name's entire purpose is to be resolved through that sink, host-pinned, exactly as bedrock-api-key is (internal/api/secrets.go explains why that one is excluded too). Nothing is stored under it — a secrets-API Put of this name is a value the sentinel arm never reads.
const CLISecretPrefix = "wardyn-cli-"
CLISecretPrefix starts every person's CLI sign-in capture: wardyn-cli-<component id>-{sso,entra}.
const ComponentConfigSchemaVersion = 1
ComponentConfigSchemaVersion is the one schema version this build reads.
const ComponentFileDelivery = true
ComponentFileDelivery says whether this build can deliver a secret as a file. While false, Validate refuses the mode, so no definition can promise a delivery nothing performs. True since the file lane exists (the file_secret grant and its dispatch).
const ComponentMCPTransportHTTP = "http"
ComponentMCPTransportHTTP is the one MCP transport a component may use: streamable HTTP.
const ImageBuildSchema = 1
ImageBuildSchema is the record version; a higher number reads as unrecorded.
const IntegrationCredentialToken = "token"
IntegrationCredentialToken is the secret ROLE a generic header-delivered credential uses; closed kinds keep their own role names ("api_key", "pat", "ssh_key").
const MachineNameMax = 253
MachineNameMax bounds a reported machine name: the longest DNS host name.
const ManagedOAuthSecret = "anthropic-managed-oauth"
ManagedOAuthSecret is a SENTINEL secret name (NOT a stored secret) that works exactly like SubscriptionOAuthSecret at the injection sink (host-pinned to api.anthropic.com, forced Authorization: Bearer, value masked), but resolves to the Wardyn-MANAGED subscription token: a long-lived `claude setup-token` OAuth token the operator captured via the container-login flow and Wardyn persisted (internal/api/harnesscred.go). This is what lets a distroless COMPOSE/containerized deployment, with no host ~/.claude to read, credential a subscription run proxy-side without ever making the token resident in the sandbox. Distinct from SubscriptionOAuthSecret only in SOURCE (managed store vs resident host ~/.claude), so the audit trail names which one credentialed a run.
const MaxCLIAzureSubscriptions = 16
MaxCLIAzureSubscriptions bounds the subscriptions one Azure CLI row pins.
const MaxMemberDrivesPerPerson = 20
MaxMemberDrivesPerPerson is the ceiling on MemberDrivePolicy.MaxPerPerson.
const ModelProviderSecretPrefix = "wardyn-provider-"
ModelProviderSecretPrefix starts every per-person credential name: wardyn-provider-<uid>-{key,oauth,sso,entra}.
const OrgPoolSelfHostedMsg = "Organization pools take remote-hosted runners only. A self-hosted runner can join its owner's user pools."
OrgPoolSelfHostedMsg refuses a self-hosted runner in an organization pool.
const PushActsAsOperator = "operator"
PushActsAsOperator is the ActsAsLabel of a git_pat whose secret is the operator's shared one rather than the run owner's own: no person owns it.
const PushContentMaxPaths = 10
PushContentMaxPaths caps the review-matched paths a PushContentScope names. PathsTotal carries the exact count; the complete list rides beside the scope as a PushPathList, stored per approval, never in the scope (the dedup key) or a refusal body.
const RecordingMaxParts = 2048
RecordingMaxParts bounds how many parts one run's session recording may be stored as (RL-12: wardyn-rec uploads a long run's cast every 24 h, or as soon as half the 64 MiB per-upload cap is waiting). It exists for the reason that cap does: a hostile sandbox must not grow its stored recording without bound, and the Recordings list reads every part of a run.
No existing limit bounds the honest part count: a governance profile may let a run have no end, and the deployment's ephemeral disk ceiling is optional. So this is a named ceiling, sized so an honest run does not reach it: 2048 parts is more than five and a half years of 24 h parts, or 64 GiB of terminal output in 32 MiB parts, and bounds one run's store at 2048 uploads of at most 64 MiB (128 GiB) and a replay or list render at 2048 reads. The control plane refuses a higher part (413), and the proxy never forwards one.
const RemoteRunnerNameMax = 64
RemoteRunnerNameMax is the longest display name of a remote-hosted runner, in characters.
const RunnerAssertedEvidenceTTL = 90 * 24 * time.Hour
RunnerAssertedEvidenceTTL is how long quarantined evidence of an asserted (org-erased) run is kept, on the runner and in the org's action rows, after its quarantine succeeded.
const RunnerAssertedSubjectCap = 100
RunnerAssertedSubjectCap bounds the asserted subjects one runner can have, settled or not. Past it the runner is held not-ready.
const RunnerPoolDefaultPrefKey = "runner_pool_default.v2"
RunnerPoolDefaultPrefKey is the principal_prefs key of a person's own default pool. A person's document sits under their authenticated principal only.
const RunnerPoolNameMax = 64
RunnerPoolNameMax is the longest pool name, in characters.
const RunnerPoolUseSubjectsMax = 64
RunnerPoolUseSubjectsMax bounds a policy's subject list.
const RunnerPostureSourceRunnerAsserted = "runner_asserted"
RunnerPostureSourceRunnerAsserted is the only posture source in 0.9: the runner said so.
const RunnerRevokeNotMine = "not_mine"
RunnerRevokeNotMine marks an owner's revoke of a runner they did not register.
const RunnerUnclaimedTTL = 24 * time.Hour
RunnerUnclaimedTTL is how long an unclaimed runner waits for its owner.
const ServicePrincipalPrefix = "service:"
ServicePrincipalPrefix begins the principal of a service identity.
const SubscriptionOAuthSecret = "anthropic-subscription-oauth"
SubscriptionOAuthSecret is a SENTINEL secret name (NOT a stored secret): an api_key grant carrying it resolves at inject time to the operator's LIVE Anthropic subscription OAuth token (resident ~/.claude), never a stored value — so subscription runs are credentialed proxy-side like api-key runs while the sandbox holds only the inert sentinel. Also the durable "this profile uses subscription LLM auth" marker on a recorded profile, since the ~/.claude mount that would otherwise signal it is never synthesized. Shared here so api + recordmode + UI agree on the name.
const UserTypeStandard = "standard"
UserTypeStandard is the id of the built-in user type the migration seeds: editable, never deletable, and the sign-in tie floor.
Variables ¶
var ( // ErrApprovalAlreadyDecided: a decision was attempted on an approval that already left PENDING. // Fail closed — never let a second decision overwrite the first. ErrApprovalAlreadyDecided = errors.New("approval already decided") // ErrDuplicatePendingApproval: a partial unique index rejected a second open PENDING approval for // the same dedup key — a dedup signal (re-read the winner), not a hard failure. ErrDuplicatePendingApproval = errors.New("duplicate pending approval") )
These sentinels live here (shared by internal/store and internal/approval) so the FSM can errors.Is instead of matching message text, which silently breaks on rewording.
var ApprovalKinds = []ApprovalKind{ ApprovalCredential, ApprovalEgressDomain, ApprovalToolCall, ApprovalCredentialReauth, ApprovalPushContent, }
ApprovalKinds is the closed set, checked by test against the constants above — closedEnumChecks once missed approvals.kind entirely, which would have let a DB-rejected constant pass every gate green.
var ApprovalNotifyStates = []ApprovalNotifyState{ NotifyPending, NotifySending, NotifySent, NotifyCancelled, NotifyDead, }
ApprovalNotifyStates is the closed set, compared with the approval_notifications.state CHECK by internal/db's closedEnumChecks.
var ApprovalStates = []ApprovalState{ ApprovalPending, ApprovalApproved, ApprovalDenied, ApprovalExpired, ApprovalCancelled, }
ApprovalStates is the closed set, in lifecycle order, so closedEnumChecks compares the DB CHECK against the Go set rather than a hand-typed test list.
var CLIGitHubRefused = []ghscope.Capability{ghscope.CapRepoAdmin, ghscope.CapOrgAdmin, ghscope.CapPackagesWrite, ghscope.CapActionsAdmin}
CLIGitHubRefused is the closed list of grantable capabilities a GitHub CLI row never carries: each changes a repository's or an organisation's settings, deletes packages, or administers Actions, and the CLI's token acts as the organisation's App rather than as the person.
var ClosedADOTokenModes = map[ADOTokenMode]bool{ ADOTokenModeBearer: true, ADOTokenModeMintedPAT: true, ADOTokenModeOwnPAT: true, }
ClosedADOTokenModes is the closed token-mode set — see ClosedGitLanes.
var ClosedCredentialSources = map[CredentialSource]bool{ CredentialSourceShared: true, CredentialSourcePerUser: true, }
ClosedCredentialSources is the closed source set — the only values a write may name.
var ClosedGitLanes = map[GitLane]bool{ GitLaneApp: true, GitLanePAT: true, GitLaneSSH: true, GitLaneEntra: true, }
ClosedGitLanes is the closed lane set — see ClosedGitProviderKinds.
var ClosedGitProviderKinds = map[GitProviderKind]bool{ GitProviderGitHub: true, GitProviderAzureDevOps: true, }
ClosedGitProviderKinds is the only kinds a write may name.
var ClosedIntegrationKinds = map[string]bool{ IntegrationKindGitHubApp: true, IntegrationKindGitHost: true, }
ClosedIntegrationKinds is the closed kind set — the kinds with bespoke behavior in code, and as of 0.5 the ONLY kinds a write may name (validateIntegrationWrite).
var ClosedModelProviderKinds = map[ModelProviderKind]bool{ ModelProviderAnthropicSubscription: true, ModelProviderBedrockSSO: true, ModelProviderAnthropicAPIKey: true, ModelProviderOpenAIAPIKey: true, ModelProviderBedrockBearer: true, ModelProviderCustomEndpoint: true, ModelProviderAzureFoundry: true, }
ClosedModelProviderKinds is the only kind set a write may name.
var ConfinementClassNames = map[ConfinementClass]string{ CC1: "Fence", CC2: "Wall", CC3: "Vault", }
ConfinementClassNames maps each wire code to its UI display label (mirrors cc-meta.ts CC_META).
var DriveBackends = []DriveBackend{ DriveBackendDockerVolume, DriveBackendHostPath, DriveBackendK8sPVC, DriveBackendK8sPVCStatic, }
DriveBackends is the closed set in the order the admin surface shows them.
var DriveObjectSchemes = []DriveObjectScheme{DriveObjectSchemeSlug, DriveObjectSchemeID}
DriveObjectSchemes is the closed set, in the order a row can only ever move through (a legacy row stays slug forever; a new row is always id).
var DriveReclaims = []DriveReclaim{DriveReclaimRetain, DriveReclaimDelete}
DriveReclaims is the closed set in admin-surface order.
var HomeTemplates = []HomeTemplate{HomeTemplateHash, HomeTemplateSub, HomeTemplateEmailLocal}
HomeTemplates is the closed set in admin-surface order.
var LegacyGitLanes = []GitLane{GitLaneApp, GitLanePAT, GitLaneSSH}
LegacyGitLanes are the lanes an empty GitProvider.Lanes list admits: not "every lane in ClosedGitLanes" but the three from when this rule was written, frozen forever so growing the closed set never silently opts a stored row into a new lane. It never applies to an Azure DevOps row: an empty list there is refused at the write boundary (the shared pat and ssh lanes it would expand to are retired), and migration 0103 rewrote every stored one.
var NonTerminalRunStates = []RunState{RunQueued, RunPending, RunStarting, RunRunning, RunWaiting}
NonTerminalRunStates is IsTerminal's complement, for the active-run quota query. POSITIVE, never `NOT IN (terminal)`: a forgotten member only undercounts here, but a negation would wedge capped accounts at their limit forever. Pinned against IsTerminal by test.
var SecretNameRE = regexp.MustCompile(`^[a-z0-9]([a-z0-9._-]{0,126}[a-z0-9])?$`)
SecretNameRE is the shape of a stored secret's name: a safe, predictable identifier, so a name that could never be stored is refused where it is typed rather than found missing at dispatch.
ponytail: internal/api still carries its own secretNameRE and validInjectionFormat with these same rules; point both at this file and delete the copies.
var SlotHoldingRunStates = []RunState{RunPending, RunStarting, RunRunning, RunWaiting}
SlotHoldingRunStates is NonTerminalRunStates minus RunQueued: the states whose runs hold a pool, deployment or host slot, and the only ones a sandbox reaper may age. A waiting run holds nothing.
var TemplateRoutes = []TemplateRoute{ {Route: "GET /api/v1/templates", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierMember, Group: TemplateTierGroupMember}, {Route: "GET /api/v1/templates/{id}", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierMember, Group: TemplateTierGroupMember}, {Route: "GET /api/v1/templates/{id}/revisions/{revision}", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierMember, Group: TemplateTierGroupMember}, {Route: "POST /api/v1/templates", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierOrgAdmin, Group: TemplateTierGroupAdmin}, {Route: "PUT /api/v1/templates/{id}", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierOrgAdmin, Group: TemplateTierGroupAdmin}, {Route: "DELETE /api/v1/templates/{id}", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierOrgAdmin, Group: TemplateTierGroupAdmin}, {Route: "POST /api/v1/templates/{id}/copy", RouterClass: "member", Person: TemplateTierOwner, Org: TemplateTierOrgAdmin, Group: TemplateTierGroupAdmin}, {Route: "POST /api/v1/templates/import", RouterClass: "member"}, {Route: "GET /api/v1/admin/template-group-admins", RouterClass: "admin"}, {Route: "PUT /api/v1/admin/template-group-admins", RouterClass: "admin"}, {Route: "DELETE /api/v1/admin/template-group-admins", RouterClass: "admin"}, }
TemplateRoutes is the route and authority table for the template surface.
Functions ¶
func ASCIIOnlySubject ¶ added in v0.7.0
ASCIIOnlySubject reports whether s contains no rune above ASCII. It answers exactly one question — can strings.ToLower move a rune across the ASCII boundary — so a control character answers true and this is NOT PrintableASCII's rule.
SECURITY: case-insensitive matching (ToLower/EqualFold) does Unicode case folding, under which a KELVIN SIGN "k" (U+212A) MATCHES ASCII "k" — so guarding the LOWERED value would let a crafted non-ASCII claim fold onto a real operator-authored ASCII entry (RoleMap, LegacyAdminEmails, a group name) and inherit everything bound to it. The guard must run on the RAW value, before the fold, everywhere an ASCII allowlist is compared case-insensitively.
func BarrierLabel ¶ added in v0.9.0
func BarrierLabel(c ConfinementClass) string
BarrierLabel is the console's name for a confinement class.
func CLIGitHubCapabilities ¶ added in v0.9.0
func CLIGitHubCapabilities(caps []ghscope.Capability) []ghscope.Capability
CLIGitHubCapabilities is caps without anything a GitHub CLI token may not hold: a capability that is not grantable, or one CLIGitHubRefused names.
func CanonicalGroupSubject ¶ added in v0.7.0
CanonicalGroupSubject canonicalizes an operator-authored group name into the exact string a session snapshot carries for it, or ok=false when no snapshot could ever carry it (empty or non-printable-ASCII).
SECURITY: guards the RAW value before folding — order is the property. strings.ToLower's Unicode case mapping (U+212A folds to 'k') would otherwise let a crafted look-alike fold onto the real ASCII group and inherit its grants.
func CanonicalPushCollection ¶ added in v0.9.0
CanonicalPushCollection is an Azure DevOps Server collection's canonical spelling: Unicode NFKC, then lower case, as Azure DevOps Server compares collection names without regard to case. Unlike a hosted organisation it may hold a space and letters outside ASCII. It refuses what Azure DevOps Server refuses in one (its reserved characters, a leading "_" or dot, two dots in a row, a trailing dot, more than 64 characters), a leading or trailing space, any other white space, a control or zero-width character, and the two letters Go and JavaScript lower-case differently (U+0130 and U+03A3), so both file one collection under one key.
func CanonicalPushHost ¶ added in v0.9.0
CanonicalPushHost is the provider host's canonical spelling: ASCII lower case, no port, no trailing dot. The SSH aliases of the two public hosts are folded into the https host (ssh.github.com, ssh.dev.azure.com), and an Azure DevOps remote's legacy hosts too (*.visualstudio.com, vs-ssh.visualstudio.com become dev.azure.com), because one forge answers to each. Any other ssh. host is its own host. A host outside ASCII is refused before anything is lower-cased or normalised, because Go and JavaScript fold such letters differently. An empty host is the provider's public host.
func CanonicalPushOrg ¶ added in v0.9.0
CanonicalPushOrg is the organisation's canonical spelling: Unicode NFKC, then ASCII lower case. It refuses what cannot be one entry's name: anything empty, longer than 100 bytes, holding white space or a control or format character (a zero-width space included), a trailing dot, or any character outside ASCII after the normalisation. GitHub owners and Azure DevOps organisations are ASCII and case-insensitive, so nothing else can name one.
func CanonicalUserSubject ¶ added in v0.7.0
CanonicalUserSubject canonicalizes an operator-authored USER subject (OIDC `sub` or email) into the stored form.
SECURITY: same guard-before-fold order as CanonicalGroupSubject, to stop a Unicode look-alike (e.g. U+212A -> 'k') folding onto a real human's subject.
Unlike the group rule it does NOT refuse non-ASCII, deliberately: a `sub` is IdP-issued identity, not an operator label, so refusing it would refuse a real person — kept unfolded and unrefused. internal/api's own user-subject boundaries and the caller's subject list (capabilitySubjects) call this same function, so what a caller can BE is exactly what an admin can WRITE. Both arms trim whitespace; a non-ASCII string stays unfolded and matches only that exact string.
func ClosedADOTokenModeList ¶ added in v0.7.10
func ClosedADOTokenModeList() []string
ClosedADOTokenModeList is ClosedADOTokenModes in a stable order.
func ClosedCredentialSourceList ¶ added in v0.7.2
func ClosedCredentialSourceList() []string
ClosedCredentialSourceList is ClosedCredentialSources in a stable order, for a rejected write's "want one of: …".
func ClosedGitLaneList ¶ added in v0.7.2
func ClosedGitLaneList() []string
ClosedGitLaneList is ClosedGitLanes in a stable order.
func ClosedGitProviderKindList ¶ added in v0.7.2
func ClosedGitProviderKindList() []string
ClosedGitProviderKindList is ClosedGitProviderKinds in a stable order.
func ClosedIntegrationKindList ¶ added in v0.5.0
func ClosedIntegrationKindList() []string
ClosedIntegrationKindList is ClosedIntegrationKinds sorted, for the "want one of: …" half of a rejected write's error.
func ClosedModelProviderKindList ¶ added in v0.8.0
func ClosedModelProviderKindList() []string
ClosedModelProviderKindList is ClosedModelProviderKinds in stable order, for a rejected write's error.
func ConfigKeyLooksSecret ¶ added in v0.9.0
ConfigKeyLooksSecret reports whether a config key names a secret by one of its words (GITHUB_TOKEN, apiKey, db-password, CLIENT_SECRET, JWT_SECRET, a secret access key). Whole words only: MAX_TOKENS and BYPASS_CACHE are plain settings, and so is SECRET_NAME, which names a stored secret. A config value is plain text by contract, so a key like this is refused by the schema and by a template; a secret travels through a secret_ref field. Only the singular "secret" counts: the plural is in carried field names (add_secrets) that hold names, never values.
func DecodeComponentConfigSchema ¶ added in v0.9.0
func DecodeComponentConfigSchema(data []byte) (ComponentConfigSchema, []ConfigIssue)
DecodeComponentConfigSchema reads one schema from JSON: bounded size, one value, no unknown field, and the validation below. Anything else is refused, including an unknown schema version.
func DeniesEveryPlacement ¶ added in v0.9.0
DeniesEveryPlacement reports whether denied rules out both placements, so the people it binds could run nothing.
func DenyPathSegments ¶ added in v0.8.0
DenyPathSegments is the one reading of a push_rules.deny_paths entry, shared by write-time validation and the broker's matcher so the two cannot disagree. A leading "/" is dropped (patterns anchor at the root either way); a trailing "/" means everything beneath (the .gitignore/CODEOWNERS spelling), so "infra/" reads as "infra/**". An empty, "." or ".." segment is refused — git never stores such a path, so it would silently match nothing and read as enforcement that isn't there. Invalid UTF-8 and leading/trailing whitespace are refused too (a near-certain typo).
func DriveGrantWidens ¶ added in v0.9.0
func DriveGrantWidens(d UserDrive, prev *UserDriveGrant, g UserDriveGrant) string
DriveGrantWidens names the field a write of g would widen past the security-admin tier, judged against drive d and the subject's current allocation prev (nil: none), or "". That tier allocates a drive with its own posture or narrower. Only a super admin newly states a home_override, which names where on the storage the files live and is compared only with other typed names, never with a derived home, or widens writability: true, or dropping a read-only override on a writable drive. An unchanged re-send on the same drive is not new, unless it enables a row that was disabled: a disabled row's home_override or writable exception is not in force, so turning the row on with one puts it in force and is the super admin's to do.
func DriveHomeName ¶ added in v0.7.0
DriveHomeName derives the per-user home segment for one principal.
PRECEDENCE: (1) override (a user-tier grant's home_override) WINS; (2) hash — `d-` + the first 20 hex of sha256(drive id + "\n" + subject), the drive id keeping one member's two drives from colliding; (3) sub / email_local — the CLAIM, lowercased, email_local truncated at the first "@".
EVERY non-hash path is checked against driveHomeSegmentRe; an unusable claim is an ERROR, NEVER A GUESS. The remedy is a home_override.
`subject` is the identity the home is derived FROM and the caller selects it — never the GRANT's subject, since a group grant's subject would give an entire group one home.
func DriveHomeStricterRuleClause ¶ added in v0.7.0
func DriveHomeStricterRuleClause(b DriveBackend) string
DriveHomeStricterRuleClause is the extra sentence a backend's home rule needs beyond the frozen DOCKER-rule refusal, said to a MEMBER (or "" when that already covers it) — a server-composed SUFFIX appended after the frozen sentence, never a reworded canon.
func DriveObjectID ¶ added in v0.8.0
DriveObjectID is the drive's own id, dashless — the fixed-width fragment DriveObjectSchemeID puts where DriveSlug would otherwise go: always 32 lowercase hex characters, unlike DriveSlug whose length depends on caller input.
func DriveObjectName ¶ added in v0.7.0
DriveObjectName is what the runner asks the substrate for, given a home already derived by DriveHomeName:
- docker_volume -> wardyn-drive-<drive-slug>-<home>
- k8s_pvc[_static] -> wardyn-drive-<drive-slug>-<home>
- host_path -> <host_root>/<home>, cleaned
EVERY name Wardyn MINTS carries the DRIVE's slug: a home_override is a literal an admin typed on the GRANT, and the grant gets re-pointed between drives, so without the slug a re-point would leave two drives naming one volume. A SHARE keeps its own shape: <host_root> already scopes it.
THE DRIVE HALF IS d.ObjectScheme, beside DriveSlug rather than replacing it (migration 0061's unique index still keys on the slug). DriveObjectSchemeID puts DriveObjectID(d.ID) there instead — 32 hex characters, always, so <home> starts at a FIXED offset no crafted name can shift (issue #163).
func DriveObjectNamedByWardyn ¶ added in v0.7.0
func DriveObjectNamedByWardyn(b DriveBackend) bool
DriveObjectNamedByWardyn reports whether WARDYN MINTS this backend's storage object name rather than joining a path somebody else already created.
IT IS NOT THE MANAGED/SHARE SPLIT: DriveKind answers "does Wardyn CREATE the object?"; this answers "does Wardyn NAME it?". k8s_pvc_static is a SHARE by the first question and Wardyn-named by the second. Only host_path is on the other side. The SAME expression DriveObjectName branches on.
func DriveSlug ¶ added in v0.7.0
DriveSlug folds a drive's human name into the DNS-1123 fragment a PVC name carries. ValidateUserDrive refuses a name that folds to nothing.
EXPORTED BECAUSE THE FOLD IS NOT INJECTIVE: "Corp NAS" and "corp nas" fold to one slug, so UNIQUE(name) alone would let two different-looking drive names mint the SAME object. Migration 0061 makes the slug column UNIQUE for every backend Wardyn names; this function decides what that column holds.
func DriveSubjectHash ¶ added in v0.7.0
DriveSubjectHash fingerprints the principal a drive object was allocated to, in the one shape a substrate LABEL may carry it: the first 20 hex of sha256(subject), folded exactly as DriveHomeName folds it.
NON-PII IS THE WHOLE REQUIREMENT: a label is echoed verbatim by `docker volume inspect`/`kubectl describe` to anybody who can reach the daemon. The drive id is NOT in this digest, unlike DriveHomeName's — the label lives beside `wardyn.drive`, which already carries the id.
An EMPTY subject returns "" rather than the digest of "": the caller then writes no label, avoiding a fingerprint every identity-less caller would share.
func FoldWorkspaceContract ¶ added in v0.5.0
func FoldWorkspaceContract( attachments []WorkspaceAttachment, sources map[uuid.UUID]Source, overlay map[string]WorkspaceRequirement, ) map[string]WorkspaceRequirement
FoldWorkspaceContract computes a workspace's effective requirements (the map applyWorkspaceRequirements consumes unchanged) from its attachments, the attached sources' own contracts, and the workspace's overlay rows. Precedence, in order:
- Each attachment contributes its source's contract; a dangling SourceID contributes nothing.
- An override of "off" drops that source's contribution for that key.
- An override lane (optional|required) replaces the source's lane.
- SECURITY: a write:<path> key whose path isn't the source's own Locator is dropped, else a shared source could claim write on a path it doesn't own and widen a sibling mount in every attaching workspace.
- Collisions merge: Level = strongest, Provenance = weakest — fail-closed since the run-create trust boundary auto-mints only operator_set secrets (ponytail: overlay is the one-row override if too strict).
- An overlay row replaces the merged value outright; it can't remove a key ("off" is for that).
Pure: no store, no clock. With no attachments (or all-ephemeral), the result is exactly the overlay.
func IsWildcardHost ¶ added in v0.9.0
IsWildcardHost reports whether an override host is spelled with a wildcard.
func ManagedBackendRejectsTemplate ¶ added in v0.7.0
func ManagedBackendRejectsTemplate(backend DriveBackend, tmpl HomeTemplate) bool
ManagedBackendRejectsTemplate is THE managed-backend home-template rule: a managed object's name ENDS in the home segment and is printed by `docker volume ls`/`kubectl get pvc` with no inspect/describe needed, so only `hash` — unique and revealing nothing — may name one. A share backend keeps every template.
Exported because the rule had TWO enforcement points that drifted apart: the write boundary widened from `email_local` to every non-hash template, but the resolver did not, so a legacy `sub` row was refused at authoring and still mounted. One predicate, so a third site cannot diverge again.
func PATRepoCovers ¶ added in v0.8.6
PATRepoCovers reports whether the repos entry key entry covers key, both from PATRepoKey. An entry covers itself, and one trailing "/*" covers exactly one further path segment: "group/*" covers "group/app" and not "group/sub/app". A wildcard entry is covered only by the same wildcard.
func PATRepoKey ¶ added in v0.8.6
PATRepoKey is the one repository-identity function for a git_pat scope. It returns the key a repos entry (or an extracted request path) compares by, and ok=false for a malformed one: empty, an empty segment, ".." anywhere, a "." segment, "?", "#", a backslash, a control character, an empty or repeated trailing "/*", or an unknown forge. An empty forge means generic.
generic returns the path exactly: no case-folding, no ".git" stripping, because git-http-backend serves different repositories for Team/App.git and team/app.git, and for team/foo and team/foo.git. A forge may canonicalise only where the proxy then forwards the canonical form, so that the compared string and the forwarded string are one value. No forge does yet, so every forge keys by the exact path.
func PATRepoSet ¶ added in v0.8.6
PATRepoSet returns a Repos value holding exactly the given entries (an empty call is the explicit empty list, "none").
func PrintableASCII ¶ added in v0.9.0
PrintableASCII reports whether every rune of s is a printable ASCII character (U+0020..U+007E). Exported for internal/auth/oidc's email-domain allowlist, which refuses a claim's domain under the same raw-before-fold rule.
func PushPathsDigest ¶ added in v0.8.0
PushPathsDigest is PushContentScope.PathsDigest over paths.
func PushRuleEntryOfRemote ¶ added in v0.9.0
PushRuleEntryOfRemote is how the git route finds the entry a remote belongs to, from the remote's own address and never from a request body. It returns the CANONICAL host and organisation (the pair PushRuleKey files rules under). The host is the remote's host with the ssh. alias of a public host folded; the organisation is:
- GitHub and GitHub Enterprise Server: the first path segment (https://host/org/repo.git, git@host:org/repo.git, ssh://git@host/org/repo).
- Azure DevOps: https://dev.azure.com/org/project/_git/repo (a user@ prefix is ignored), https://org.visualstudio.com/project/_git/repo (host becomes dev.azure.com), and, on the SSH hosts only, git@ssh.dev.azure.com:v3/org/project/repo and git@vs-ssh.visualstudio.com:v3/org/project/repo.
- Azure DevOps Server (any other host): the collection, in [basePath/]Collection[/Project]/_git/Repo. Without the provider row's base path only Collection/Project/_git/Repo is read; a server under a prefix such as /tfs needs it (PushRuleEntryOfRemoteAt), or the remote is an error, never every collection under one entry.
A path segment that is empty, "." or "..", or that holds an encoded "/", is refused: git and curl resolve them, so the push would go somewhere else than the entry derived here. A remote whose entry cannot be derived is an error, which the caller must treat as a refusal and never as "no rules".
func PushRuleEntryOfRemoteAt ¶ added in v0.9.0
PushRuleEntryOfRemoteAt is PushRuleEntryOfRemote for a provider row whose Azure DevOps Server lives under basePath (for example "/tfs").
func PushRuleEntryOfServerRemote ¶ added in v0.9.0
PushRuleEntryOfServerRemote is PushRuleEntryOfRemoteAt for the callers that have the provider row's base path in hand, and the one both the run's dispatch plan and the git route use, so the two can never key one remote differently. Everything but an Azure DevOps Server remote is PushRuleEntryOfRemoteAt's.
An Azure DevOps Server remote is read from its "_git" segment, never by counting from the front: the project is the segment right before "_git", and the segments between the row's base path and the project are the collection. After the base path exactly one such segment may remain when the base path itself names no collection, and none when it does (the collection is then the base path's last segment). Any other shape is an error, never a guess, because a guess files the push under another collection's rules.
A base path of two segments names its collection (prefix and collection). Residual: a base path of one segment is a collection or a prefix such as "/tfs", and the row does not say which, so the shape of the remote decides: a collection segment beyond it makes the base a prefix, none makes it the collection. A collection-only row therefore cannot tell a remote with one segment too many from a prefix row's.
func PushRuleKey ¶ added in v0.9.0
PushRuleKey is the identity of an SCM entry for push rules, "host/org", both canonical, or an error saying what cannot be one entry.
func RemoteRunnerDefaultName ¶ added in v0.9.0
RemoteRunnerDefaultName is the name a configured remote-hosted runner gets when its row is made.
func ShareBackendRejectsTemplate ¶ added in v0.7.0
func ShareBackendRejectsTemplate(backend DriveBackend, tmpl HomeTemplate) bool
ShareBackendRejectsTemplate is the MIRROR rule: a SHARE's directories are named by whoever owns the share, and Wardyn never mkdir's on one, so a `hash` home names a directory that will never exist. The empty template is excluded on both sides: ValidateUserDrive defaults it before this is asked.
func SplitRequirementKey ¶ added in v0.5.0
SplitRequirementKey splits a "<type>:<key>" requirement key on the first colon only (a write:<path> key may itself contain colons) and reports whether the type token is known.
func SuggestedRunnerMaxRuns ¶ added in v0.9.0
SuggestedRunnerMaxRuns is what registration offers when no Runs at once is given: about half the host's CPU cores, at least one, at most the ceiling.
func ValidComponentName ¶ added in v0.8.9
ValidComponentName checks a component's display name: 1 to MaxComponentNameRunes printable characters with no surrounding space, so two names that read the same are the same row.
func ValidInjectionFormat ¶ added in v0.8.9
ValidInjectionFormat is the rule for the value template of a proxy-injected credential header. The secret is substituted for its one %s, so a template with none sends a bare prefix and drops the credential, one with two renders a formatting error onto the wire, and a line break is a header-splitting shape. Empty is valid: the caller chooses the default.
func ValidMachineName ¶ added in v0.9.0
ValidMachineName reports whether s may be stored as a runner's machine name: at most MachineNameMax bytes of printable characters. "" (none reported) is valid.
func ValidRemoteRunnerID ¶ added in v0.9.0
ValidRemoteRunnerID reports whether id can name a remote-hosted runner.
func ValidRunnerMaxRuns ¶ added in v0.9.0
ValidRunnerMaxRuns reports whether n is a Runs at once a self-hosted runner may hold.
func ValidToolEffect ¶ added in v0.7.0
func ValidToolEffect(e ToolEffect) bool
ValidToolEffect reports whether e is one of the three effects. A closed enum, checked at every ingest point: an unrecognised effect must be a 400, never a silently-ignored rule that reads as enforcement.
func ValidateComponentRefs ¶ added in v0.8.9
func ValidateComponentRefs(refs []ComponentRef, validHost func(string) error) error
ValidateComponentRefs checks a run request's components. Whether each one exists and may be attached is the caller's.
func ValidateDriveName ¶ added in v0.9.0
ValidateDriveName is the one name rule for admin and member drives. It checks strings.TrimSpace(name), which is the form the caller stores.
func ValidateHarnessCredentials ¶ added in v0.9.0
func ValidateHarnessCredentials(creds []HarnessCredential) error
ValidateHarnessCredentials is the consumer's structural check: a harness and a digest each once, and a digest that is 64 hex digits.
func ValidateHarnessToolRules ¶ added in v0.9.0
func ValidateHarnessToolRules(sets []HarnessToolRules) error
ValidateHarnessToolRules is the structural check a consumer of the carrier makes: a harness once, a closed effect, no rule named "*" (the default has its own field) and no tool named twice.
func ValidatePushRuleSets ¶ added in v0.9.0
func ValidatePushRuleSets(sets []PushRuleSet) error
ValidatePushRuleSets is the structural check a consumer of the carrier makes before trusting it: a known provider, an organisation, one set per entry, and path patterns the broker can match. Numeric bounds are the authoring door's (validatePushRules in internal/api).
func ValidateRemoteRunnerName ¶ added in v0.9.0
ValidateRemoteRunnerName accepts a printable, trimmed, single-line name.
func ValidateRunnerPoolName ¶ added in v0.9.0
ValidateRunnerPoolName accepts a printable, trimmed, single-line name.
func ValidateRunnerPoolUseSubjects ¶ added in v0.9.0
func ValidateRunnerPoolUseSubjects(subjects []RunnerPoolSubject) error
ValidateRunnerPoolUseSubjects checks a policy's subjects: one to RunnerPoolUseSubjectsMax, each a user, group or user_type with a printable name, none repeated. The "all" subject is refused: it is no narrowing.
func ValidateUserDrive ¶ added in v0.7.0
ValidateUserDrive normalizes d in place and validates it: trim, length, no control characters. runnerTarget must match d.Backend.RunnerTarget(), refused here rather than left to fail at run time; host_root is not checked against the deployment's env ceiling (see UserDriveHostRootCheck), since this package cannot read the environment.
func ValidateUserDriveGrant ¶ added in v0.7.0
func ValidateUserDriveGrant(g *UserDriveGrant) error
ValidateUserDriveGrant normalizes g in place and validates it: trim, length, no control characters, and for a GROUP subject the shared CanonicalGroupSubject the login-time snapshot is built with, so the subject vocabulary matches capabilitySubjects exactly.
Types ¶
type ADOEntraConfig ¶ added in v0.7.10
type ADOEntraConfig struct {
// TenantID is the Entra tenant every sign-in for this row goes to (GUID); a well-known alias ("common", "organizations") is refused.
TenantID string `json:"tenant_id"`
ClientID string `json:"client_id"` // app registration the sign-in names, as a GUID
// CapabilityCeiling is the widest capability set a run on this row may
// ever hold; required and non-empty when the block is present.
CapabilityCeiling []adoscope.Capability `json:"capability_ceiling,omitempty"`
DefaultProfile []adoscope.Capability `json:"default_profile,omitempty"` // what a run gets by default; empty reads as adoscope.ProfileDefault
TokenMode ADOTokenMode `json:"token_mode,omitempty"` // empty reads as ADOTokenModeBearer
// PATMaxHours is the longest a minted_pat run's token lives (1-168). 0
// reads as 8; read via PATHours, never directly.
PATMaxHours int `json:"pat_max_hours,omitempty"`
// PATMaxDays is the furthest expiry an own_pat token may carry (1-90). 0
// reads as 30; read via PATDays, never directly. A Server row has no entra
// block, so it cannot set this and always reads the default, 30.
PATMaxDays int `json:"pat_max_days,omitempty"`
// RESTAPI: brokered REST calls, or only git traffic. Default true; read
// via RESTAPIEnabled, never directly.
RESTAPI *bool `json:"rest_api,omitempty"`
// RunnerResident opts this connection's minted tokens in to runner_resident
// delivery: held in a self-hosted runner's OS keystore for the run and
// injected by the runner's own proxy, where the class's delivery policy and
// posture requirement allow it. Off by default.
RunnerResident bool `json:"runner_resident,omitempty"`
// RunnerResidentSandbox also hands that token into the sandbox, where the
// agent can use it past the REST gate and capability holds. Requires
// RunnerResident.
RunnerResidentSandbox bool `json:"runner_resident_sandbox,omitempty"`
}
ADOEntraConfig is the Entra lane's configuration on one Azure DevOps row: tenant, app, widest ceiling, default profile, and token presentation. The ceiling is not a default: CapabilityCeiling is the most a run could ever be granted; DefaultProfile is what it gets by default.
func (*ADOEntraConfig) PATDays ¶ added in v0.8.2
func (c *ADOEntraConfig) PATDays() int
PATDays is PATMaxDays with the unset default applied.
func (*ADOEntraConfig) PATHours ¶ added in v0.8.2
func (c *ADOEntraConfig) PATHours() int
PATHours is PATMaxHours with the unset default applied.
func (*ADOEntraConfig) Profile ¶ added in v0.7.10
func (c *ADOEntraConfig) Profile() []adoscope.Capability
Profile is the capabilities a run gets when it asks for nothing: DefaultProfile, or adoscope.ProfileDefault when that is empty.
func (*ADOEntraConfig) RESTAPIEnabled ¶ added in v0.7.10
func (c *ADOEntraConfig) RESTAPIEnabled() bool
RESTAPIEnabled: the rest_api default (unset or nil receiver) is enabled.
type ADOTokenMode ¶ added in v0.7.10
type ADOTokenMode string
ADOTokenMode is how a run presents itself to Azure DevOps on an Entra-lane row: the Entra access token itself, a personal access token Wardyn creates for the run, or one the person pasted. A named field, so a refused mode says why rather than an opaque "unknown field" 400.
const ( ADOTokenModeBearer ADOTokenMode = "bearer" // sends the Entra access token itself; the zero value // ADOTokenModeMintedPAT creates a short-lived personal access token per // run, in the person's name, through the sign-in app's own grant. Named // and closed, but the write boundary still refuses it: it is not yet // available. ADOTokenModeMintedPAT ADOTokenMode = "minted_pat" // ADOTokenModeOwnPAT is a personal access token the person pasted in // themselves. It needs no tenant or client: nothing signs in. SO EVERY // PICKER OF THE ONE SIGN-IN ROW MUST SKIP IT — the console-login capture // (adoEntraRow in cmd/wardynd) and the dispatch row lookup take any // enabled per_user entra-lane row today, and a row with no tenant or // client would fail their validation on every login and could shadow the // row that does sign in. Skipping own_pat there is L1's change (#1428). ADOTokenModeOwnPAT ADOTokenMode = "own_pat" )
func (ADOTokenMode) Valid ¶ added in v0.7.10
func (m ADOTokenMode) Valid() bool
Valid reports whether m is an accepted mode.
type APIToken ¶ added in v0.6.0
type APIToken struct {
ID uuid.UUID `json:"id"`
Principal string `json:"principal"`
Email string `json:"email,omitempty"`
Role string `json:"role"`
UserType string `json:"user_type"`
Groups []string `json:"groups,omitempty"`
GroupsTruncated *bool `json:"groups_truncated,omitempty"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"` // nil = never expires; an expired token authenticates nothing
MintedBy string `json:"minted_by,omitempty"` // admin who minted this for its owner; empty when the owner minted it
// Disposition is where an admin-minted token stands, derived at read (#1508); empty on any other token.
Disposition TokenDisposition `json:"disposition,omitempty"`
// ServiceIdentity names the service identity an admin linked this token to, set with Disposition on
// a token that has a move row (#1508); empty on any other token.
ServiceIdentity string `json:"service_identity,omitempty"`
Token string `json:"token,omitempty"` // plaintext, create response ONLY
// IdentityStampedAt is when Role and Groups were last stamped: at mint, then at each sign-in of
// the owner. Nil reads as stale under WARDYN_ROLE_STAMP_TTL. Never on the wire.
IdentityStampedAt *time.Time `json:"-"`
// RevSeq is the revocation sequence read when the token was minted; migration 0176 gave the tokens
// that existed their order once. 0 only for a token written by a replica that predates it, which a
// revocation cutoff orders on CreatedAt. Never on the wire.
RevSeq int64 `json:"-"`
}
APIToken is a per-user, long-lived bearer credential a human mints for scripts/CI, replacing shared use of the deployment-wide admin token; grants/RBAC/ownership bind to the owning human. Email/Role/Groups are a snapshot of the creating session (a bearer token carries no ID token to re-derive them from) — same bounded-stale ceiling as SSHPublicKey.Role: a demotion doesn't reach an outstanding token; revoke it. Groups distinguishes nil ("snapshot unavailable") from empty ("IdP sent no usable groups"); UserType change on the People page revokes tokens rather than re-stamping them. Token carries the plaintext credential, populated only on the create response — never stored, listed or logged. GroupsTruncated is THREE-VALUED: true = cut off by the cookie byte cap at mint; false = complete; nil = unknown (pre-dates this bit) and reads as TRUNCATED (fail closed) — nil-as-false would let a holder silently shed a group-assigned governance profile.
type ActorType ¶
type ActorType string
ActorType distinguishes who performed an action in the audit stream.
type AgentProvider ¶ added in v0.7.2
type AgentProvider struct {
// ID is the --agent value this row governs: a harness-catalog id OR a
// WARDYN_AGENT_IMAGES key. The image-map half is deliberate: harnessByID
// documents an image-map-only custom agent as supported, so a block that
// refuses agents without a row must let the admin write one for their own.
ID string `json:"id"`
// Disabled turns the row off without deleting it — negative-sense, so the
// zero value is ENABLED. Its runs are refused and the console renders it
// unavailable, never hidden.
Disabled bool `json:"disabled,omitempty"`
// DefaultProvider names the model provider a new run of this agent uses
// unless the person chooses another; must be enabled for this agent. If
// later disabled, the default becomes a sentinel whose runs are refused,
// never silently moved to another provider.
DefaultProvider string `json:"default_provider,omitempty"`
}
AgentProvider is one agent row: which agent, whether it is offered, and the model provider its runs use unless the person chooses another. How a run reaches its model, and whose credential that is, live on the model provider (ModelProvider); every credential is each person's own.
type AgentProviders ¶ added in v0.7.2
type AgentProviders struct {
// Agents are the per-agent rows; an agent with NO row is not offered at
// all once the block exists.
Agents []AgentProvider `json:"agents,omitempty"`
}
AgentProviders is the org's AGENT-ENABLEMENT POLICY: which coding agents this deployment offers and the model provider each uses by default. A sub-object of SiteConfig, shaped like WorkspaceProviders (see that type for the no-DDL / MDM-deliverable doctrine).
It exists because availability was an image map with no auth semantics: a customer's admin captured one AWS SSO session that silently backed every member's runs, with no way for a member to see whose credential was dying.
THE ZERO VALUE IS LEGACY OPEN MODE: with no block, every predicate over it answers exactly what it answered before the block existed (see the api package's agentProvidersConfigured / agentProviderFor).
VALIDATION LIVES IN internal/api (validateAgentProviders): a row's id is checked against the harness catalog / boot image map, and its default against the model providers — server state this package can't see. This file is types and closed enums only.
func (*AgentProviders) Empty ¶ added in v0.7.2
func (p *AgentProviders) Empty() bool
Empty reports whether p carries no policy — the shape a PUT uses to CLEAR the block. The write boundary normalizes this back to nil so a later GET doesn't render "agent_providers":{} forever.
type AgentRun ¶
type AgentRun struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by"` // human principal (token `sub`)
Agent string `json:"agent"` // e.g. "claude-code", "codex-cli"
Repo string `json:"repo"` // e.g. "org/name"
Task string `json:"task"` // human task description
// Title groups runs in the console's list; empty for legacy/system runs, which fall back to Task.
Title string `json:"title,omitempty"`
// Description is optional free-text context; display-only, never interpreted.
Description string `json:"description,omitempty"`
PolicyID *uuid.UUID `json:"policy_id,omitempty"`
ConfinementClass ConfinementClass `json:"confinement_class"`
State RunState `json:"state"`
SPIFFEID string `json:"spiffe_id"` // spiffe://<trust-domain>/agent-run/<id>; empty while QUEUED, written when the run starts
RunnerTarget string `json:"runner_target"` // "docker"
// Placement is where the sandbox lives (OD-8): remote (the organisation's own
// executor), local (a runner the person registered), or empty for a run from
// before 0.9 and for one whose placement nothing has recorded yet. ALWAYS on
// the wire — a run has a placement even when the value is the empty one.
Placement Placement `json:"placement"`
// PlacementFilled records that a placement was RESOLVED for this run rather
// than asked for by name, so an incident review can tell "the person chose
// local" from "local was the only eligible placement".
PlacementFilled bool `json:"placement_filled,omitempty"`
// RunnerID names the runner a local run landed on. NULL for a remote run and
// for every run from before 0.9; omitted on the wire when NULL, never "".
RunnerID *uuid.UUID `json:"runner_id,omitempty"`
// RunnerName, RunnerOnline and RunnerLastSeenAt are read-side facts about that runner, projected on
// the run routes and never stored. They are absent for a remote run and when the runner could not be
// read. The name is the runner's current one, which equals the name at create: a runner is never
// renamed and a claimed row is never deleted, so a revoked runner's run keeps it. RunnerOnline is
// this process's live view, so a pointer: absent is unknown. false is a fact on a single replica (also
// when the hub cannot be asked); under WARDYN_HA it is never sent, because a replica that does not hold
// the runner's session cannot tell offline from connected elsewhere.
RunnerName string `json:"runner_name,omitempty"`
RunnerOnline *bool `json:"runner_online,omitempty"`
RunnerLastSeenAt *time.Time `json:"runner_last_seen_at,omitempty"`
// EvidenceSource says who can attest this run's evidence (OD-8): substrate or
// runner_asserted, empty for a legacy or unresolved record. Not
// `confinement_source`, which says who CHOSE the class.
EvidenceSource RunEvidenceSource `json:"evidence_source,omitempty"`
// RunnerPoolID is the runner pool the run was admitted into, and the next three fields say how
// (all written once at create). A pool is a label for a choice and never admission: the run
// goes to the exact runner recorded in RunnerID (a self-hosted runner) or RunnerPoolExecutor (a
// remote-hosted runner's id), whatever the pool or anyone's defaults say later. Nil for a run from
// before pools and for a server-originated run that names none; RunnerPoolSelection is then empty.
RunnerPoolID *uuid.UUID `json:"runner_pool_id,omitempty"`
RunnerPoolRevision int64 `json:"runner_pool_revision,omitempty"`
RunnerPoolSelection RunnerPoolSelection `json:"runner_pool_selection,omitempty"`
RunnerPoolExecutor string `json:"remote_runner_id,omitempty"`
// Reserved is what the run holds on its runner while it holds a sandbox there (0174): the CPU and
// memory it was given and its main drive, written once at insert and summed as the runner's In use.
Reserved RunnerAmounts `json:"-"`
SandboxRef string `json:"sandbox_ref,omitempty"`
Image string `json:"image,omitempty"` // resolved sandbox image, kept for provenance; empty for legacy rows
// Interactive marks a human-driven run: sandbox comes up RUNNING, no task exec'd, no completion watcher.
Interactive bool `json:"interactive"`
// Experience is the canonical run mode chosen at launch (0.9 New Run), frozen at create and never re-derived. Empty for a run from an older client or before 0.9;
// BackgroundOnly is the one predicate every interactive door reads. It is not Interactive, which is the legacy execution flag.
Experience RunExperience `json:"experience,omitempty"`
// WorkspacePath is the run's primary host directory, denormalized so the control plane can warn (never block) a second run on it.
WorkspacePath string `json:"workspace_path,omitempty"`
// WorkspaceID marks a governed scan run: the driver runs wardyn-scan instead of the agent, trusted (not sandbox input) to persist the profile.
WorkspaceID *uuid.UUID `json:"workspace_id,omitempty"`
// WorkspaceIDs is the onboarded workspaces this run's policy referenced at create; grants nothing, only tells an `always` egress decision what to persist to.
WorkspaceIDs []uuid.UUID `json:"workspace_ids,omitempty"`
SourceID *uuid.UUID `json:"source_id,omitempty"` // trusted run→library-source linkage for a per-source scan run; never set by user runs
// AutoStopAfterSec is the run's idle auto-stop cap, frozen at create so the reaper works with no policy_id to JOIN. 0 = never; <0 = explicit never.
AutoStopAfterSec int `json:"auto_stop_after_sec,omitempty"`
AgentExecID string `json:"agent_exec_id,omitempty"` // docker exec id, so the crash reconciler can check agent liveness across a wardynd restart
FailureHint string `json:"failure_hint,omitempty"` // operator-facing reason a run FAILED before the agent started; display-only
// StatusDetail is what the substrate says this run awaits while STARTING, and a lifetime end reason ("lifetime: end_reached", "lifetime: max_lifetime_reached") on a run KILLED at its end or its maximum; blanked on leaving STARTING except a TERMINAL failure reason or a lifetime end reason (kept for postmortem).
StatusDetail string `json:"status_detail,omitempty"`
StatusReason string `json:"status_reason,omitempty"` // reason token parsed out of StatusDetail, derived at read, never stored
// AutonomyLevel freezes the level resolved at create, not the profile/posture that produced it (that's on the audit row).
AutonomyLevel AutonomyLevel `json:"autonomy_level,omitempty"`
// AdmittedAt is when a queued run was promoted out of the queue; written by promotion only, so nil for a run that never waited.
AdmittedAt *time.Time `json:"admitted_at,omitempty"`
// Queue is a waiting run's place in its pool's queue, derived at read from the queue row and never stored on the run; absent once the run leaves QUEUED.
Queue *RunQueue `json:"queue,omitempty"`
EndsAt *time.Time `json:"ends_at"` // run's lease end, captured once at create from RunLimits, never re-resolved
// LifetimeCapAt is the latest end the pool and the deployment allow, measured from launch (created_at plus the lowest of their maximums); absent when neither bounds the lifetime or for a run from before this column. LifetimeCapSource names which one bound it (pool or deployment) and stays off the wire.
LifetimeCapAt *time.Time `json:"lifetime_cap_at,omitempty"`
LifetimeCapSource LimitSource `json:"-"`
WaitBudgetSec int `json:"wait_budget_sec,omitempty"` // how long an approval this run raises stays open; 0 = deployment default alone applies
// RunLimits are the owner's profile limits as they stood at create; later clamps use these captured bounds, never the profile's current ones.
RunLimits RunLimits `json:"run_limits"`
GovernanceProfileID *uuid.UUID `json:"governance_profile_id,omitempty"`
// LostAt/LostReason mark a run that lost its sandbox but is kept (no proxy/network, files stay); stays RUNNING until a kill or the ended-run grace.
LostAt *time.Time `json:"lost_at,omitempty"`
LostReason LostReason `json:"lost_reason,omitempty"`
// ContainmentError/At: latest/first stop-confirm failure for a kept run; the lease sweep retries and clears both once it lands.
ContainmentError string `json:"containment_error,omitempty"`
ContainmentErrorAt *time.Time `json:"containment_error_at,omitempty"`
// EndedAt stamps the transition into a terminal RunState; a lease-ended run stays RUNNING until the ended-run grace, so its end time is LostAt, not EndedAt.
EndedAt *time.Time `json:"ended_at,omitempty"`
EndTightenedAt *time.Time `json:"end_tightened_at,omitempty"` // profile re-clamp shortened the end
// PausedAt/PausedReason mark a run frozen with nobody present; keeps state/memory/files/proxy until activity or resume. Not contained — kill still works.
PausedAt *time.Time `json:"paused_at,omitempty"`
PausedReason PauseReason `json:"paused_reason,omitempty"`
ActiveAt *time.Time `json:"active_at,omitempty"` // presence clock: last human input/egress decision/proxied bytes, or a resume; keepalives never move it; nil reads as CreatedAt
// ModelProviderID freezes the provider id at create; the kind is deliberately NOT frozen (can change later) and lives only on the run.create audit snapshot.
ModelProviderID string `json:"model_provider_id,omitempty"`
UserType string `json:"user_type,omitempty"` // creator's resolved user type, frozen at create
Preset string `json:"preset,omitempty"` // launch preset this run was expanded from; empty for an explicit-spec run
PresetVersion int `json:"preset_version,omitempty"`
OperatorOwned bool `json:"operator_owned,omitempty"` // mirrors identity.Claims.OperatorOwned so dispatch/revive re-mint read what create decided; the console reads it for the no-personal-owner entry carve-out
// CreatedVia is the registered portal this run was launched through on the person's behalf; nil when self-launched. The owner is still the person.
CreatedVia *uuid.UUID `json:"created_via,omitempty"`
// HasRecording/RecordingBytes/RecordingDurationSec are derived, never stored, projected only with ?include=recording_meta; HasRecording is the only "no recording" signal.
HasRecording bool `json:"has_recording"`
RecordingBytes int64 `json:"recording_bytes,omitempty"`
RecordingDurationSec float64 `json:"recording_duration_sec,omitempty"`
DiskMiB int `json:"disk_mib,omitempty"` // effective ephemeral disk cap MiB; 0 = no cap resolved
Attention *RunAttention `json:"attention,omitempty"` // what this live run awaits and who can clear it; derived, only on view-opted-in read paths
// HumanAuthor is the principal accountable for the run at create, written once: the service identity's sponsor
// at launch for a service identity's run, unchanged by a later sponsor change, and CreatedBy for any other run. A
// record of accountability, not proof that a person was present. Empty for a run from before it was recorded.
// Read by the second-human rules only.
HumanAuthor string `json:"-"`
// LaunchIntent is the setup kept for Run again (a kind Run document), written in the run row's own
// transaction when set. Never on the wire and never read back onto a listed run: the creator reads it by
// run id. On a created run it is set only when the store kept it.
LaunchIntent json.RawMessage `json:"-"`
}
AgentRun is one governed execution of a coding agent on behalf of a human, with its own identity (SPIFFE ID), credential grants, and audit trail.
func (AgentRun) BackgroundOnly ¶ added in v0.9.0
BackgroundOnly is the one predicate every interactive door of a run calls (attach, interactive exec, SSH, the UI gateway, a desktop session, a restored session, and the same routes through the API and CLI). It reads the stored Experience, never the request and never the legacy Interactive flag.
An empty Experience is a run launched by an older client or before 0.9: it keeps today's documented behaviour at every door, so this answers false. A new client always carries an experience, so its Background runs answer true.
type ApprovalDecision ¶ added in v0.5.0
type ApprovalDecision struct {
State ApprovalState
DecidedBy string
Reason string
Scope ApprovalScope // meaningful only for an egress_domain approval; others leave the zero value
ExpiresAt *time.Time
}
ApprovalDecision is what a human (or the sweeper, for a stale-PENDING expiry) is deciding, threaded as one value instead of growing DecideApproval's signature. ExpireStale leaves Scope at "" (never ScopeRun) — an expiry is a sweep nobody decided.
type ApprovalEscalation ¶ added in v0.8.6
ApprovalEscalation is one pending approval's place on its notification schedule: the highest tier whose due time has passed, and the earliest due time still ahead (nil when no later tier exists).
type ApprovalKind ¶
type ApprovalKind string
ApprovalKind enumerates what a human is being asked to approve.
const ( ApprovalCredential ApprovalKind = "credential" ApprovalEgressDomain ApprovalKind = "egress_domain" ApprovalToolCall ApprovalKind = "tool_call" // ApprovalCredentialReauth: the run's model credential lapsed mid-run, so the proxy holds the // next exchange while the owner signs in again. Not a decision — the row moves to APPROVED only // via ResolveReauth, never approval.decide. ApprovalCredentialReauth ApprovalKind = "credential_reauth" // ApprovalPushContent: a brokered git push touched a require_review_paths path, held for an // admin decision. Admin-only — a member deciding their own push is the exfiltration it stops. ApprovalPushContent ApprovalKind = "push_content" )
type ApprovalNotifyChannelStat ¶ added in v0.8.6
type ApprovalNotifyChannelStat struct {
Channel string
LastSentAt *time.Time
LastError string
LastErrorAt *time.Time
FailedLastHour int
}
ApprovalNotifyChannelStat is one channel's delivery history read from the outbox: when a send last succeeded, the class and time of the last failed attempt, and how many rows went dead in the last hour. LastError is a failure class such as "http_status:503", never text a server or URL wrote.
type ApprovalNotifyState ¶ added in v0.8.6
type ApprovalNotifyState string
ApprovalNotifyState is the lifecycle of one approval_notifications outbox row.
const ( NotifyPending ApprovalNotifyState = "pending" NotifySending ApprovalNotifyState = "sending" NotifySent ApprovalNotifyState = "sent" NotifyCancelled ApprovalNotifyState = "cancelled" NotifyDead ApprovalNotifyState = "dead" )
type ApprovalRequest ¶
type ApprovalRequest struct {
ID uuid.UUID `json:"id"`
RunID uuid.UUID `json:"run_id"`
GrantID *uuid.UUID `json:"grant_id,omitempty"`
Kind ApprovalKind `json:"kind"`
RequestedScope json.RawMessage `json:"requested_scope"`
State ApprovalState `json:"state"`
RequestedAt time.Time `json:"requested_at"`
DecidedAt *time.Time `json:"decided_at,omitempty"`
DecidedBy string `json:"decided_by,omitempty"`
MintedJTI string `json:"minted_jti,omitempty"`
Reason string `json:"reason,omitempty"`
// DecisionScope is how far the decision reaches (see ApprovalScope); empty on a PENDING row. Named
// decision_scope to avoid clashing with RequestedScope (the unrelated host JSON).
DecisionScope ApprovalScope `json:"decision_scope,omitempty"`
// DecisionExpiresAt is set only when DecisionScope is until — a different clock than the EXPIRED-state sweeper, which ages out stale PENDING requests.
DecisionExpiresAt *time.Time `json:"decision_expires_at,omitempty"`
// ExpiresAt is when a PENDING request stops waiting: min(requested_at+WaitBudgetSec, EndsAt); computed on read, never stored.
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Held bool `json:"held,omitempty"` // Held/HeldUntil project approval.Hold(this, now) at response time, never stored; both zero on a DECIDED row
HeldUntil *time.Time `json:"held_until,omitempty"`
// EscalationTier is the highest notification tier whose due time has passed, and SLADueAt is the
// next tier's due time. Both are projected from approval_notifications at response time on PENDING
// rows only, never stored; tier 0 (the first notice) and "no later tier" leave them zero.
EscalationTier int16 `json:"escalation_tier,omitempty"`
SLADueAt *time.Time `json:"sla_due_at,omitempty"`
}
ApprovalRequest is a blocking human-in-the-loop gate. RequestedScope is EXACTLY what the approver saw; the broker writes MintedJTI back in the same transaction as the mint.
type ApprovalScope ¶ added in v0.5.0
type ApprovalScope string
ApprovalScope is how far a human's approve/deny decision reaches — orthogonal to FirstUseMode, which decides whether to escalate at all. egress_domain carries any of the four; tool_call carries none; credential carries only ScopeRun, re-mintable for the rest of the run.
const ( // ScopeOnce releases exactly one connection — one CONNECT tunnel for HTTPS, one request for HTTP. ScopeOnce ApprovalScope = "once" // ScopeRun holds for the rest of the run — the legacy default meaning. ScopeRun ApprovalScope = "run" // ScopeUntil holds until DecisionExpiresAt or run end, whichever comes first. ScopeUntil ApprovalScope = "until" // ScopeAlways also persists the host onto the run's workspace so future runs inherit it. Operator-only. ScopeAlways ApprovalScope = "always" )
func (ApprovalScope) Normalize ¶ added in v0.5.0
func (s ApprovalScope) Normalize() ApprovalScope
Normalize resolves a stored/wire value for every runtime read: empty means legacy run-scoped => ScopeRun; an unknown non-empty value can only come from a newer control plane, and treating it as ScopeRun would be fail-open across versions, so it floors to ScopeOnce. Caveat: on a DENY, once is actually WIDER (it re-raises), so this isn't uniformly fail-closed, just re-denied with extra queue noise.
func (ApprovalScope) Valid ¶ added in v0.5.0
func (s ApprovalScope) Valid() bool
Valid reports whether s is empty (legacy run-scoped) or a known scope — rejects garbage at the API write boundary; runtime reads still fail closed via Normalize.
type ApprovalState ¶
type ApprovalState string
ApprovalState is the approval lifecycle.
const ( ApprovalPending ApprovalState = "PENDING" ApprovalApproved ApprovalState = "APPROVED" ApprovalDenied ApprovalState = "DENIED" ApprovalExpired ApprovalState = "EXPIRED" // ApprovalCancelled: the run reached a terminal state while still PENDING; answering it would // mint a credential for a gone sandbox. Every reader treats it as a refusal. ApprovalCancelled ApprovalState = "CANCELLED" )
type ArtifactOverride
deprecated
type ArtifactOverride struct {
BaseURL string `json:"base_url"`
TokenSecretRef string `json:"token_secret_ref,omitempty"`
}
ArtifactOverride is one ecosystem's corporate artifact-registry redirect: the base URL to emit into that ecosystem's config (.npmrc/pip.conf/cargo config/settings.xml/GOPROXY/nuget.config) plus an optional secret ref for a token injected proxy-side (the sandbox never holds the value).
Deprecated: superseded by EgressRedirect. See SiteConfig.ArtifactOverrides for why the type is kept.
type AttentionBy ¶ added in v0.8.0
type AttentionBy string
AttentionBy names who the run is waiting on, in the viewer's own console view (User or Admin; see RunAttention for how the two differ).
const ( // AttentionYou: the viewer themselves can act (decide the approval, sign // in, revive the run). AttentionYou AttentionBy = "you" // AttentionOwner: only the run's owner can act — reauth and lost-run // revive are never delegable — and the viewer isn't the owner; reachable // only in Admin view, since User view forces owner=me. AttentionOwner AttentionBy = "owner" // AttentionAdmin: only a security operator can act; the viewer is a // member who cannot decide this run's held approval. AttentionAdmin AttentionBy = "admin" )
type AttentionKind ¶ added in v0.8.0
type AttentionKind string
AttentionKind names what a live run is waiting on. Absent (RunAttention nil) means the run needs nobody — terminal, lease-ended, or nothing PENDING held.
const ( // AttentionApproval: a held approval (any kind but credential_reauth) is // parking the sandbox. AttentionApproval AttentionKind = "approval" // AttentionReauth: the run's model credential lapsed mid-run; the proxy // holds the next credential exchange for its owner to sign in. AttentionReauth AttentionKind = "reauth" // AttentionADOConsent: same as AttentionReauth, specifically an Azure // DevOps sign-in or consent request (TallyReauthADOSignIn/Consent). AttentionADOConsent AttentionKind = "ado_consent" // AttentionLost: the run lost its sandbox for a reason other than lease // end (reboot, outage) and needs a revive. AttentionLost AttentionKind = "lost" )
type AuditEvent ¶
type AuditEvent struct {
ID uuid.UUID `json:"id"`
Time time.Time `json:"time"`
RunID *uuid.UUID `json:"run_id,omitempty"`
ActorType ActorType `json:"actor_type"`
Actor string `json:"actor"` // human sub, agent SPIFFE ID, or component name
Action string `json:"action"` // dotted verb, e.g. "credential.mint", "egress.deny", "kernel.process.exec"
Target string `json:"target,omitempty"`
Outcome string `json:"outcome"` // "success" | "failure" | "denied"
SourceIP string `json:"source_ip,omitempty"`
Data json.RawMessage `json:"data,omitempty"`
// DeviceID names the enrolled device that forwarded this row; derived on read, never taken from a caller.
DeviceID *uuid.UUID `json:"device_id,omitempty"`
// PrevHash/RowHash form the tamper-evidence chain: RowHash = SHA-256(PrevHash || canonical serialization
// of the fields above), computed BY POSTGRES in a BEFORE INSERT trigger, never by the caller; sinks use
// the head hash to detect truncation. Verified via GET /api/v1/audit/chain/verify.
PrevHash string `json:"prev_hash,omitempty"`
RowHash string `json:"row_hash,omitempty"`
}
AuditEvent is one append-only audit record. Every credential mint/revoke, approval decision, policy change, egress decision, and lifecycle change emits one, carrying the delegation chain (human sub + agent run). credential.*/identity.*/approval.*/policy.*/egress.*/run.*/recording.* are control-plane and agent self-report events; kernel.* is the eBPF/Tetragon ground-truth stream, emitted ONLY by the host-scoped sensor via POST /api/v1/internal/groundtruth, which FORCES actor_type=system+actor="wardyn-tetragon-ingest" and rejects any action without the "kernel." prefix. kernel.* Data carries stream/subtype/cgroup_id/container_id, a kind-specific argv/dst/path, loader (the documented mmap execve-hook bypass surface, flagged not blocked), and correlation ("mapped"|"unmapped" — unmapped => run_id NULL, never silently dropped). Outcome stays within "success"|"failure"|"denied": kernel events use failure for an escape/coverage-gap signal and never denied — a detection stream, it does not block. See internal/groundtruth.
type AuditRetentionDrop ¶ added in v0.8.6
type AuditRetentionDrop struct {
Partition string `json:"partition"`
Rows int64 `json:"rows"`
SeqLo int64 `json:"seq_lo"`
SeqHi int64 `json:"seq_hi"`
Digest string `json:"digest"`
// EventSeq is the seq of the chained audit.retention.partition_dropped event.
EventSeq int64 `json:"event_seq"`
}
AuditRetentionDrop is a completed drop.
type AuditRetentionPartition ¶ added in v0.8.6
type AuditRetentionPartition struct {
Name string `json:"name"`
// Lo is absent for the legacy partition (it starts at MINVALUE).
Lo *time.Time `json:"lo,omitempty"`
Hi *time.Time `json:"hi,omitempty"`
Rows int64 `json:"rows"`
State string `json:"state"` // closed | open | future
// Eligible is true when audit_retention_drop would take this partition now; otherwise Refusal is the
// reason it would refuse with.
Eligible bool `json:"eligible"`
Refusal string `json:"refusal,omitempty"`
}
AuditRetentionPartition is one partition of the audit log as audit_retention_partitions reports it.
type AuditRetentionPolicy ¶ added in v0.8.6
type AuditRetentionPolicy struct {
// Days is the stored policy; EffectiveDays is what the drop function applies now (the pending value
// once its date has passed).
Days int `json:"days"`
EffectiveDays int `json:"effective_days"`
// PendingDays and PendingEffectiveAt are set together, for a decrease that has not taken effect.
PendingDays *int `json:"pending_days,omitempty"`
PendingEffectiveAt *time.Time `json:"pending_effective_at,omitempty"`
}
AuditRetentionPolicy is the persisted retention policy (audit_partition_meta). A decrease, including 0 -> finite, is pending until its date; days 0 is forever.
type AuditRetentionPolicyChange ¶ added in v0.8.6
type AuditRetentionPolicyChange struct {
Outcome string `json:"outcome"`
AuditRetentionPolicy
}
AuditRetentionPolicyChange is audit_retention_set_policy's answer. Outcome is applied, pending, cancelled or unchanged.
type AuditRetentionStatus ¶ added in v0.8.6
type AuditRetentionStatus struct {
Policy AuditRetentionPolicy `json:"policy"`
// Cutover is when the log was converted to partitions; everything before it is in the legacy partition.
Cutover time.Time `json:"cutover"`
Partitions []AuditRetentionPartition `json:"partitions"`
// MonthsAhead is how many months past the current one already have a partition.
MonthsAhead int `json:"months_ahead"`
}
AuditRetentionStatus is what GET /audit/retention reports.
type AutonomyEgressPosture ¶ added in v0.8.0
type AutonomyEgressPosture string
AutonomyEgressPosture is one of the three egress states an AutonomyRubric caps against (internal/composer/autonomy.go's posture arithmetic, #97).
const ( AutonomyEgressOpen AutonomyEgressPosture = "open" AutonomyEgressReviewed AutonomyEgressPosture = "reviewed" AutonomyEgressSealed AutonomyEgressPosture = "sealed" )
type AutonomyLevel ¶ added in v0.8.0
type AutonomyLevel string
AutonomyLevel is one rung on the autonomy ladder a governance profile's AutonomyRubric caps against, L0 (most supervised) through L3 (least). The codes stay internal — the console renders plain labels, like ConfinementClass's CC1/CC2/CC3 (0.8 #77):
- L0 "attended": interactive only, supervised seeding.
- L1 "gated": adds non-interactive runs, but tool approvals derive to hold.
- L2 "unattended": adds auto-approval and seeded auto tools.
- L3: adds task_mode=exec, the door around every other gate — the top rung.
const ( AutonomyL0 AutonomyLevel = "L0" AutonomyL1 AutonomyLevel = "L1" AutonomyL2 AutonomyLevel = "L2" AutonomyL3 AutonomyLevel = "L3" )
func (AutonomyLevel) Rank ¶ added in v0.8.0
func (l AutonomyLevel) Rank() int
Rank orders AutonomyLevel weakest (most supervised) -> strongest (least), mirroring ConfinementClass.Rank(): resolving a rubric folds several applicable caps to their MINIMUM level (#97), and Rank is what "minimum" compares on. An unrecognised value ranks below L0 so it never wins a min() against a real level.
func (AutonomyLevel) Valid ¶ added in v0.8.0
func (l AutonomyLevel) Valid() bool
Valid reports whether l is one of the four defined rungs. Unlike ConfinementClass (whose every Rank() caller already tolerates rank 0), AutonomyRubric needs this gate: an author-facing field, so a typo must 400 rather than silently rank as "below L0".
type AutonomyPosture ¶ added in v0.8.0
type AutonomyPosture struct {
Egress AutonomyEgressPosture `json:"egress"`
Secrets AutonomySecretsPosture `json:"secrets"`
Confinement ConfinementClass `json:"confinement"`
}
AutonomyPosture is a run's three-axis shape — egress reach, secret power, enforced confinement class — the input resolveRunAutonomy (#97) folds against a profile's AutonomyRubric. Computed, never stored on its own; it travels inside AutonomyResolution.
type AutonomyResolution ¶ added in v0.8.0
type AutonomyResolution struct {
Level AutonomyLevel `json:"level"`
Posture AutonomyPosture `json:"posture"`
BoundBy []string `json:"bound_by,omitempty"`
}
AutonomyResolution is what resolveRunAutonomy (#97) decides for one run: the level, the posture that produced it, and every rubric field that bound the result — provenance for the create audit row, the frozen AgentRun.AutonomyLevel, and the preflight response.
BoundBy is a LIST, deliberately: the fold is a min() over three axes, so rows routinely TIE at the resolved level, and naming just one cause would leave an admin editing a row that can't move the level while other causes stay hidden — every tied cause is named, in the fixed field order internal/composer/autonomy.go folds in.
Empty when nothing capped the level (no profile, no rubric, or an unset posture).
type AutonomyRubric ¶ added in v0.8.0
type AutonomyRubric struct {
// EgressOpen caps the level when egress is OPEN: allow-all, or any allowlisted host beyond baseline.
EgressOpen AutonomyLevel `json:"egress_open,omitempty"`
// EgressReviewed caps the level when egress is REVIEWED: first-use approval raises approvals, nothing wide open.
EgressReviewed AutonomyLevel `json:"egress_reviewed,omitempty"`
// EgressSealed caps the level when egress is SEALED: neither of the above.
EgressSealed AutonomyLevel `json:"egress_sealed,omitempty"`
// SecretsPowerful caps the level when the run holds a POWERFUL secret: a write-capable grant, an
// api_key to a non-baseline host, or a git_pat/ssh_key/env_secret grant.
SecretsPowerful AutonomyLevel `json:"secrets_powerful,omitempty"`
// SecretsBaseline caps the level when the run holds any grant, none of them powerful.
SecretsBaseline AutonomyLevel `json:"secrets_baseline,omitempty"`
// SecretsNone caps the level when the run holds no grant at all.
SecretsNone AutonomyLevel `json:"secrets_none,omitempty"`
// ConfinementCC1/CC2/CC3 cap the level by the run's ENFORCED confinement
// class (an empty enforced class reads as CC1 — see AutonomyPosture).
ConfinementCC1 AutonomyLevel `json:"confinement_cc1,omitempty"`
ConfinementCC2 AutonomyLevel `json:"confinement_cc2,omitempty"`
ConfinementCC3 AutonomyLevel `json:"confinement_cc3,omitempty"`
// AgentGuardrailLocks selects the locked L2 managed-settings document for a run that resolves to L2:
// repository hooks and permission rules cannot override the agent-side guardrails. It caps no level
// and changes nothing at any other rung.
AgentGuardrailLocks bool `json:"agent_guardrail_locks,omitempty"`
}
AutonomyRubric maps a run's posture to a permitted AutonomyLevel: three egress postures, three secret postures, three confinement classes, each unset (caps nothing) or one of the four levels. A run resolves to the MINIMUM over every field whose posture applies (internal/composer/autonomy.go, #97); an all-unset rubric caps nothing, like a nil rubric.
It also carries one boolean, AgentGuardrailLocks, that caps no level: it selects the locked L2 managed-settings document and is not part of the nine-field fold.
A closed struct with `omitempty` on every field, not a map — same doctrine as GovernanceLimits: small, complete, validated by the Go type (Validate), no DB CHECK needed.
func (AutonomyRubric) Validate ¶ added in v0.8.0
func (a AutonomyRubric) Validate() error
Validate reports the first field carrying an undefined AutonomyLevel, naming it — governanceLimitsRefusal prefixes the field name onto its 400 so an admin knows which of the nine to fix. An empty field is always valid: unset caps nothing.
type AutonomySecretsPosture ¶ added in v0.8.0
type AutonomySecretsPosture string
AutonomySecretsPosture is one of the three secret-power states an AutonomyRubric caps against.
const ( AutonomySecretsPowerful AutonomySecretsPosture = "powerful" AutonomySecretsBaseline AutonomySecretsPosture = "baseline" AutonomySecretsNone AutonomySecretsPosture = "none" )
type AzureSettings ¶ added in v0.8.6
type AzureSettings struct {
Endpoint string `json:"endpoint,omitempty"`
Route string `json:"route,omitempty"`
}
AzureSettings is an azure_foundry row's data-plane endpoint and the one route it serves (AzureRoute*).
type BaseImageEntry ¶ added in v0.5.0
type BaseImageEntry struct {
ID uuid.UUID `json:"id"`
Kind string `json:"kind"` // "registry" | "custom" | "byo"
Name string `json:"name"`
Image string `json:"image"` // the ref: the image itself (registry/byo) or the FROM (custom)
// Steps are Dockerfile lines on a "custom" Image; not applied at build
// time (host-side build RCE — see WorkspaceBaseImage.Steps), stored only
// so the row round-trips.
Steps []string `json:"steps,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
BaseImageEntry is one tier-2 catalog row: a shared, reusable image an operator saved. Kind is registry|custom|byo, never "recommended" (a derived per-workspace build with no catalog identity, enforced by a store CHECK).
type BedrockSettings ¶ added in v0.8.0
type BedrockSettings struct {
Region string `json:"region,omitempty"`
BaseURL string `json:"base_url,omitempty"`
SSOStartURL string `json:"sso_start_url,omitempty"`
SSOAccountID string `json:"sso_account_id,omitempty"`
SSORoleName string `json:"sso_role_name,omitempty"`
}
BedrockSettings is a Bedrock kind's region, optional data-plane address, and (bedrock_sso only) the org-level sign-in setup. The start URL and pin are ADMIN-OWNED: a sign-in proposes, the provider disposes.
type Branding ¶ added in v0.8.0
type Branding struct {
OrgName string
NameFormat string
Primary string
PrimaryText string
DarkPrimary string
DarkPrimaryText string
SupportURL string
Logo []byte
LogoType string
LogoFromFile bool // the logo is the site config's branding.logo_path file (0105_branding_logo_from_file)
UpdatedAt time.Time
UpdatedBy string
}
Branding is the org-wide console branding record (migration 0091_branding). Only the primary colour, its text colour, the logo and the name are brandable: danger/warning/success/info and the admin-view cue are fixed Wardyn tokens a brand can never recolour. Empty DarkPrimary means the dark pair is derived (DeriveDarkPrimary in internal/api).
type CLIAWS ¶ added in v0.9.0
type CLIAWS struct {
StartURL string `json:"start_url"` // the access portal, https
SSORegion string `json:"sso_region"` // where the portal lives
AccountID string `json:"account_id"` // 12 digits
RoleName string `json:"role_name"` // the permission set name
Region string `json:"region"` // the CLI's default region
}
CLIAWS pins one IAM Identity Center account and permission set.
type CLIAzure ¶ added in v0.9.0
type CLIAzure struct {
Subscriptions []string `json:"subscriptions"` // 1..MaxCLIAzureSubscriptions GUIDs
Access string `json:"access"` // read (GET and HEAD only) | write
}
CLIAzure pins subscriptions and whether the CLI may change them.
func (CLIAzure) SubscriptionIDs ¶ added in v0.9.0
SubscriptionIDs is the row's subscriptions as Azure compares them: lowercase GUIDs, in the row's order. An entry that is not a GUID is "".
type CLIGitHub ¶ added in v0.9.0
type CLIGitHub struct {
Capabilities []ghscope.Capability `json:"capabilities"`
}
CLIGitHub scopes the GitHub CLI to the run's repositories by capability.
type CapabilityEffect ¶ added in v0.6.0
type CapabilityEffect string
CapabilityEffect is a grant's direction. Closed and complete; DENY BEATS ALLOW at resolution time, with no user-vs-group precedence — "Bob's user allow overrode the group deny" is a breach report, not a feature.
const ( CapabilityAllow CapabilityEffect = "allow" CapabilityDeny CapabilityEffect = "deny" )
func (CapabilityEffect) Valid ¶ added in v0.6.0
func (e CapabilityEffect) Valid() bool
Valid reports whether e is allow or deny.
type CapabilityGrant ¶ added in v0.6.0
type CapabilityGrant struct {
ID uuid.UUID `json:"id"`
SubjectType CapabilitySubjectType `json:"subject_type"`
Subject string `json:"subject"`
Capability string `json:"capability"`
Value string `json:"value"`
Effect CapabilityEffect `json:"effect"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by,omitempty"`
}
CapabilityGrant is one row of the permissioning grant list: "subject S may (or may not) use capability C at value V".
Capability is a PLAIN STRING, not an enum, deliberately: the closed kind set lives in one Go slice (internal/api's capabilityKinds) and is checked at the write boundary, so a new kind needs no schema change. An unknown kind is simply inert.
Value's meaning is per kind: host or "*.suffix" for egress_host, else an exact secret name/workspace uuid/image ref; "*" is always the wildcard.
type CapabilitySubjectType ¶ added in v0.6.0
type CapabilitySubjectType string
CapabilitySubjectType names WHO a capability grant is written against. Closed and complete — its DB CHECK is pinned against these constants by internal/db's TestClosedEnumChecksMatchConstants.
const ( // CapabilitySubjectUser is one human, named by lowercased OIDC "sub" or // email — either matches, so an admin can record whichever identity they // actually know. CapabilitySubjectUser CapabilitySubjectType = "user" // CapabilitySubjectGroup is one entry of the login-time union of the ID // token's roles+groups claims (oidc.Session.Groups); Entra App Roles are // grantable through this with no extra config. CapabilitySubjectGroup CapabilitySubjectType = "group" // CapabilitySubjectAll is every signed-in human, the baseline when an IdP // emits no usable groups claim. Subject is "" for this type. CapabilitySubjectAll CapabilitySubjectType = "all" // CapabilitySubjectUserType is everyone of one user type (UserType.ID); a // person holds exactly one, stamped at sign-in. It sits between group and // all for governance ceilings/drives; for grants it's one more subject, // so a DENY on a type is a wall no user or group allow lifts. CapabilitySubjectUserType CapabilitySubjectType = "user_type" )
func (CapabilitySubjectType) Valid ¶ added in v0.6.0
func (t CapabilitySubjectType) Valid() bool
Valid rejects a garbage subject type at the API write boundary, mirroring ApprovalScope.Valid.
type CeilingOverlay ¶ added in v0.8.6
type CeilingOverlay struct {
AllowedDomains *[]string `json:"allowed_domains,omitempty"`
DeniedDomains *[]string `json:"denied_domains,omitempty"`
AllowAllEgress *bool `json:"allow_all_egress,omitempty"`
FirstUseApproval *FirstUseMode `json:"first_use_approval,omitempty"`
FirstUseHoldSeconds *int `json:"first_use_hold_seconds,omitempty"`
MaxHolds *int `json:"max_holds,omitempty"`
AllowedMethods *[]string `json:"allowed_methods,omitempty"`
MinConfinementClass *ConfinementClass `json:"min_confinement_class,omitempty"`
EligibleGrants *[]GrantSpec `json:"eligible_grants,omitempty"`
AutoStopAfterSec *int `json:"auto_stop_after_sec,omitempty"`
WorkspaceMounts *[]WorkspaceMount `json:"workspace_mounts,omitempty"`
WorkspaceRepos *[]WorkspaceRepo `json:"workspace_repos,omitempty"`
LLMInspection *LLMInspectionSpec `json:"llm_inspection,omitempty"`
UIApps *[]UIApp `json:"ui_apps,omitempty"`
Resources *ResourcesOverlay `json:"resources,omitempty"`
ToolRules *[]ToolRule `json:"tool_rules,omitempty"`
GitPushAnyBranch *bool `json:"git_push_any_branch,omitempty"`
PushRules *PushRulesOverlay `json:"push_rules,omitempty"`
AzureDevOpsCapabilities *[]adoscope.Capability `json:"azure_devops_capabilities,omitempty"`
GitHubCapabilities *[]ghscope.Capability `json:"github_capabilities,omitempty"`
}
CeilingOverlay is the part of a RunPolicySpec a composed governance profile narrows, with PRESENCE typed: every field is a pointer, so "the overlay says nothing about this" (nil, inherit the base) and "the overlay says this is empty" (a pointer to an empty list, a value) can never be confused. That distinction is load-bearing: allowed_domains [] narrows to no domains, while allowed_methods [] would read as "all methods" in the proxy and is refused.
It mirrors RunPolicySpec field for field (composer's coverage guard fails on a field of either that the other lacks). Decoded strictly: an unknown key is an error, never a silently ignored narrowing.
func DecodeCeilingOverlay ¶ added in v0.8.6
func DecodeCeilingOverlay(raw []byte) (CeilingOverlay, error)
DecodeCeilingOverlay strictly decodes a stored or submitted overlay.
type Component ¶ added in v0.8.9
type Component struct {
ID uuid.UUID `json:"id"`
Owner string `json:"owner,omitempty"`
Name string `json:"name"`
Definition ComponentDefinition `json:"definition"`
// Version starts at 1 and moves by one on every update.
Version int `json:"version"`
CreatedBy string `json:"created_by,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Component is one stored custom component: an org row (Owner "") an admin wrote and grants, or a person's saved row (Owner is their principal).
type ComponentCLI ¶ added in v0.9.0
type ComponentCLI struct {
Kind string `json:"kind"` // azure | aws | github
Azure *CLIAzure `json:"azure,omitempty"`
AWS *CLIAWS `json:"aws,omitempty"`
GitHub *CLIGitHub `json:"github,omitempty"`
}
ComponentCLI is the credential an org row gives a run's command-line tool. Kind names which of the three arms is set, and only that one is.
type ComponentConfigSchema ¶ added in v0.9.0
type ComponentConfigSchema struct {
SchemaVersion int `json:"schema_version"`
Groups []ConfigGroup `json:"groups,omitempty"`
Fields []ConfigField `json:"fields"`
}
ComponentConfigSchema is a component's field schema.
type ComponentConfigValues ¶ added in v0.9.0
type ComponentConfigValues map[string]json.RawMessage
ComponentConfigValues are the values an attachment supplies, keyed by field ID, each typed by its field's kind.
type ComponentDefinition ¶ added in v0.8.9
type ComponentDefinition struct {
Hosts []string `json:"hosts"`
Secrets []ComponentSecret `json:"secrets,omitempty"`
Config map[string]string `json:"config,omitempty"`
// CLI marks an org row as a CLI credential card (azure, aws or github).
CLI *ComponentCLI `json:"cli,omitempty"`
// MCP makes the component a remote MCP server, which a run's harnesses
// reach only through the proxy's broker route (component_mcp.go).
MCP *ComponentMCP `json:"mcp,omitempty"`
}
ComponentDefinition is the whole contract of a custom component: the hosts it reaches, the secrets it carries with how each is delivered, and plain (non-secret) environment. It holds secret names, never values.
func (ComponentDefinition) Validate ¶ added in v0.8.9
func (d ComponentDefinition) Validate(validHost func(string) error, personDefined bool) error
Validate checks the shape of a definition. It is the one rule every door that accepts a definition runs; every error names the field.
validHost is the egress allowlist's entry rule (proxy.ValidDomainEntry). It is a parameter because that package imports this one.
personDefined is true for a definition a person wrote — an inline one, or a stored row with an owner — and false for an org row (owner ""). A person's hosts must be DNS names, never IP addresses, and a person may not use Shared or PlainHTTP; org rows keep the admin's allowlist semantics.
Left to the caller, because they depend on the deployment or the store: whether the named secrets exist and whose they are, reserved secret names, and every bound on the hosts themselves (deny lists, model hosts, hosts that already carry a credential — compare with Destination).
type ComponentDelivery ¶ added in v0.8.9
type ComponentDelivery struct {
Mode string `json:"mode"` // header | env | file
// Host, Header and Format apply to header: Host is a bare host — no
// port, never a wildcard — that one of the definition's hosts names bare
// or with :443; an empty Header or Format takes the caller's default.
Host string `json:"host,omitempty"`
Header string `json:"header,omitempty"`
Format string `json:"format,omitempty"`
// PlainHTTP lets a header credential travel without TLS. That it appears
// on an org row only is the caller's check.
PlainHTTP bool `json:"plain_http,omitempty"`
// Var applies to env: the environment variable name.
Var string `json:"var,omitempty"`
// File applies to file: a single name, never a path. The directory is
// fixed by the runner.
File string `json:"file,omitempty"`
}
ComponentDelivery says how one secret reaches the run; the mode decides which other fields apply.
type ComponentKind ¶ added in v0.8.9
type ComponentKind string
ComponentKind names a thing a run can be given access to. Every kind but ComponentCustom is a closed, built-in kind with its own code; a component enforces nothing itself — it describes primitives policy and the proxy already enforce.
const ( ComponentAgent ComponentKind = "agent" ComponentModelProvider ComponentKind = "model_provider" ComponentGitProvider ComponentKind = "git_provider" // github | azure_devops ComponentGitPAT ComponentKind = "git_pat" // other forges ComponentSSHKey ComponentKind = "ssh_key" ComponentWorkspace ComponentKind = "workspace" ComponentDrive ComponentKind = "drive" // ComponentCustom is the one open kind, and the only one that is stored. ComponentCustom ComponentKind = "custom" )
type ComponentMCP ¶ added in v0.9.0
type ComponentMCP struct {
// URL is the server's streamable HTTP address: https, no userinfo, its
// host one of the component's hosts.
URL string `json:"url"`
Transport string `json:"transport"` // http
}
ComponentMCP is a remote MCP server. The run's harnesses never reach URL itself: each is configured with the proxy's broker route, which adds the server's header credential, applies the run's tool rules to its tools and is the only network path to the server.
type ComponentRef ¶ added in v0.8.9
type ComponentRef struct {
ID *uuid.UUID `json:"id,omitempty"`
Inline *ComponentDefinition `json:"inline,omitempty"`
Name string `json:"name,omitempty"`
// Builtin names a built-in Git provider component, github or azure_devops:
// API and git-over-HTTPS access to the listed repositories without a
// workspace, the agent cloning for itself. Org and Repos scope it, Access
// narrows it.
Builtin string `json:"builtin,omitempty"`
// Org is the GitHub owner or the Azure DevOps organisation. Required with Builtin.
Org string `json:"org,omitempty"`
// Repos are owner/name (GitHub) or project/repository (Azure DevOps). GitHub
// requires at least one; an Azure DevOps ref without any reaches every
// repository the person can reach in Org.
Repos []string `json:"repos,omitempty"`
// Access is read or write; empty reads as read.
Access string `json:"access,omitempty"`
// MCPHarnesses are the included harnesses (tools[].id) an MCP component's
// server is turned on for. Absent means every harness that can take it.
MCPHarnesses []string `json:"mcp_harnesses,omitempty"`
}
ComponentRef attaches one component to a run: a stored one by ID, a run-only definition given inline (never stored as a component), or a built-in Git provider (Builtin; OD-4). Name labels an inline one.
type ComponentSecret ¶ added in v0.8.9
type ComponentSecret struct {
SecretName string `json:"secret_name"`
// Shared marks an org component's secret as the operator's value rather
// than the launching person's own. Header delivery only (Validate); that
// it appears on an org row only is the caller's check, since a definition
// does not know its owner.
Delivery ComponentDelivery `json:"delivery"`
}
ComponentSecret is one secret a component carries.
type ComponentSettings ¶ added in v0.8.9
type ComponentSettings struct {
// RequireVaultForCredentials restores the CC3 confinement floor for a
// component's header credential to a non-baseline host, org and person rows
// alike. False (default) lifts the floor for component-authored grants only.
RequireVaultForCredentials bool `json:"require_vault_for_credentials,omitempty"`
// DenyResidentDelivery refuses env-var and file delivery DEPLOYMENT-WIDE,
// for org and person rows alike; header delivery keeps working.
DenyResidentDelivery bool `json:"deny_resident_delivery,omitempty"`
// AutonomyCap caps a run carrying a SELF-DEFINED component, with or without
// a governance profile: "" no cap (default), "L1" holds tool calls, "L0"
// refuses unattended runs. No other value is accepted: the cap only tightens.
AutonomyCap AutonomyLevel `json:"autonomy_cap,omitempty"`
}
ComponentSettings is SiteConfig.Components. Every field's zero value is the owner-decided default, so an absent block and an all-zero one mean the same thing (PUT /site-config stores the latter as the former).
type ConcurrencyBound ¶ added in v0.9.0
type ConcurrencyBound struct {
Source LimitSource `json:"source"`
Max int `json:"max"`
}
ConcurrencyBound is one source's cap on running runs. The caps are never folded into one number, because each counts a different set: the pool's counts the pool's runs across everyone, governance's one person's runs, the deployment's every run.
type ConfigBindTarget ¶ added in v0.9.0
type ConfigBindTarget string
ConfigBindTarget is what a field's value feeds on the component.
const ( // ConfigBindConfig is a ComponentDefinition.Config key (a plain, // non-secret environment value). ConfigBindConfig ConfigBindTarget = "config" // ConfigBindSecret is the stored secret delivered into one environment // variable. ConfigBindSecret ConfigBindTarget = "secret" )
type ConfigBinding ¶ added in v0.9.0
type ConfigBinding struct {
Target ConfigBindTarget `json:"target"`
// Key is the environment variable name: the config key, or the variable
// the secret is delivered in. It follows the component env-name rules,
// including the reserved names.
Key string `json:"key"`
}
ConfigBinding names the component target a field feeds.
type ConfigCondition ¶ added in v0.9.0
type ConfigCondition struct {
Field string `json:"field"`
Equals json.RawMessage `json:"equals"`
}
ConfigCondition is "Field equals Equals". It is the only expression the schema has.
type ConfigField ¶ added in v0.9.0
type ConfigField struct {
// ID is the field's structural name: lowercase letters, digits and
// underscores. Values are keyed by it.
ID string `json:"id"`
Kind ConfigFieldKind `json:"kind"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
Group string `json:"group,omitempty"`
// Required asks for a value whenever the field is visible and has no default.
Required bool `json:"required,omitempty"`
// ReadOnly shows a managed value. It needs a Default, and a value that
// differs from it is refused.
ReadOnly bool `json:"read_only,omitempty"`
// Default is the value an untouched field takes, typed by Kind.
Default json.RawMessage `json:"default,omitempty"`
// Min and Max bound an integer or number.
Min *float64 `json:"min,omitempty"`
Max *float64 `json:"max,omitempty"`
// MaxLen bounds a string or each string_list entry, in bytes.
MaxLen int `json:"max_len,omitempty"`
// MaxItems bounds a string_list.
MaxItems int `json:"max_items,omitempty"`
// Options are an enum's choices.
Options []ConfigOption `json:"options,omitempty"`
// VisibleWhen shows the field only while another field holds one value.
VisibleWhen *ConfigCondition `json:"visible_when,omitempty"`
Bind ConfigBinding `json:"bind"`
}
ConfigField is one setting.
type ConfigFieldKind ¶ added in v0.9.0
type ConfigFieldKind string
ConfigFieldKind is the type of one field's value.
const ( ConfigKindString ConfigFieldKind = "string" ConfigKindBoolean ConfigFieldKind = "boolean" ConfigKindInteger ConfigFieldKind = "integer" ConfigKindNumber ConfigFieldKind = "number" ConfigKindEnum ConfigFieldKind = "enum" ConfigKindStringList ConfigFieldKind = "string_list" // ConfigKindSecretRef picks one of the person's stored secrets by name. // It never holds a secret value. ConfigKindSecretRef ConfigFieldKind = "secret_ref" )
The closed field kinds. Object and nested kinds are deliberately absent: a field is one value, and layout is groups.
type ConfigGroup ¶ added in v0.9.0
type ConfigGroup struct {
ID string `json:"id"`
Label string `json:"label"`
Description string `json:"description,omitempty"`
}
ConfigGroup is a labelled section the form lays fields out in.
type ConfigIssue ¶ added in v0.9.0
ConfigIssue is one problem in a schema or a set of values.
func ValidateComponentConfigSchema ¶ added in v0.9.0
func ValidateComponentConfigSchema(s ComponentConfigSchema) []ConfigIssue
ValidateComponentConfigSchema checks a schema against the closed vocabulary and its bounds.
func ValidateComponentConfigValues ¶ added in v0.9.0
func ValidateComponentConfigValues(s ComponentConfigSchema, values ComponentConfigValues, looksSecret func(string) bool) []ConfigIssue
ValidateComponentConfigValues checks one attachment's values against a valid schema. Each supplied value is typed and bounded whether or not its field is visible. A required field is asked for only while visible and without a default; a managed (read-only) field accepts only its default. looksSecret refuses a string that is credential-shaped (a secret reference is a stored secret's name, never its value); this package cannot import the scanner, so the caller passes it, and a nil scanner is itself refused so no caller can opt out of the scan.
type ConfigOption ¶ added in v0.9.0
ConfigOption is one enum choice.
type ConfinementClass ¶
type ConfinementClass string
ConfinementClass declares how strongly a sandbox substrate confines an agent; policy may refuse high-trust credential scopes to weak classes. See threatmodel/.
const ( // CC1: hardened shared-kernel runc (userns, seccomp, AppArmor, cap-drop). CC1 ConfinementClass = "CC1" // CC2: gVisor userspace kernel (the default — needs a native Docker engine with runsc registered). CC2 ConfinementClass = "CC2" // CC3: Kata microVM (requires /dev/kvm). CC3 ConfinementClass = "CC3" )
func (ConfinementClass) Rank ¶
func (c ConfinementClass) Rank() int
Rank orders classes weakest→strongest; unrecognised values rank 0 so a minimum-class gate fails closed. Matching is exact — normalise untrusted input first.
type CredentialDeliveryPolicyDoc ¶ added in v0.9.0
type CredentialDeliveryPolicyDoc struct {
Policy json.RawMessage `json:"policy"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by"`
}
CredentialDeliveryPolicyDoc is the stored local_credential_delivery document. Policy is the JSON of placement.DeliveryPolicy, validated by the writer; the store keeps it opaque.
type CredentialGrant ¶
type CredentialGrant struct {
ID uuid.UUID `json:"id"`
RunID uuid.UUID `json:"run_id"`
CreatedAt time.Time `json:"created_at"`
Spec GrantSpec `json:"spec"`
// Delivery is how the grant reached a local run; empty for a remote run and for one from before 0.9.
Delivery GrantDelivery `json:"delivery,omitempty"`
}
CredentialGrant records what a run is ELIGIBLE for — not issuance: minting happens only via the broker, and for RequiresApproval grants only inside the transaction that verifies an APPROVED ApprovalRequest for this run+scope.
type CredentialSource ¶ added in v0.7.2
type CredentialSource string
CredentialSource says WHOSE credential a git provider's lanes use.
const ( // every run. The zero value, so an unset field keeps legacy behaviour. CredentialSourceShared CredentialSource = "shared" // CredentialSourcePerUser: one credential per principal, captured or // stored under their own namespace. CredentialSourcePerUser CredentialSource = "per_user" )
func (CredentialSource) Valid ¶ added in v0.7.2
func (s CredentialSource) Valid() bool
Valid reports whether s is one of the two sources.
type Delegate ¶ added in v0.8.0
type Delegate struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
IdPClientID string `json:"idp_client_id"`
Group string `json:"group"`
// Never serialized: a credential hash has no business leaving the process.
CredentialSHA256 string `json:"-"`
RegisteredBy string `json:"registered_by"`
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
// Credential is the plaintext portal credential, on the register
// response ONLY; the row stores its hash.
Credential string `json:"credential,omitempty"`
}
Delegate is a registered portal (migration 0094, #1142): a trusted front-end that may exchange a signed-in person's own identity-provider token for a short delegated token acting for that person. IdPClientID is the portal's own client id at the identity provider, which a subject token must name; Group is the one group a person must be in for the portal to act for them. Revoking the row (RevokedAt) ends every delegated token it minted at once.
type DelegatedToken ¶ added in v0.8.0
type DelegatedToken struct {
ID uuid.UUID `json:"id"`
DelegateID uuid.UUID `json:"delegate_id"`
Principal string `json:"principal"`
Email string `json:"email,omitempty"`
UserType string `json:"user_type"`
Groups []string `json:"groups"`
GroupsTruncated bool `json:"groups_truncated"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
// RevSeq is the revocation sequence read when the token was minted; 0 for a token minted before it
// existed, which a revocation cutoff orders on CreatedAt. Never on the wire.
RevSeq int64 `json:"-"`
}
DelegatedToken is one live token a Delegate was handed for one person: the identity it replays (always at the user role — a portal never carries admin reach) and when it stops working. Stored only by hash, never refreshed.
type DelegationVia ¶ added in v0.8.0
DelegationVia names the portal and the delegated token an act was performed through. It is the audit row's data.via and an attach ticket's replayed origin; nil everywhere a person acted for themselves.
type Destination ¶ added in v0.8.9
type Destination struct {
// Host is lower case with no trailing dot; an IP literal is in its
// canonical spelling. For a wildcard it is the suffix after "*.".
Host string
Wildcard bool
// Port 0 means any port, as a bare entry does at the proxy.
Port int
}
Destination is one parsed egress entry — "api.example.com", "*.example.com", either with ":port" — in the one normal form every comparison of a component host against another destination uses: the model-provider veto, the injection-collision check, and dispatch. A gate parses and vetoes a component's hosts BEFORE ComponentDefinition.Validate, so a spelling variant of a vetoed host is refused for what it reaches, not for how it is spelled. The parse is the proxy's own (domainmatch.Classify), so a comparison here can never disagree with what the proxy will match.
func MCPURLDestination ¶ added in v0.9.0
func MCPURLDestination(raw string) (Destination, error)
MCPURLDestination checks an MCP server URL and returns its host and port as a destination (Port 0 for the standard https port).
func ParseDestination ¶ added in v0.8.9
func ParseDestination(entry string) (Destination, error)
ParseDestination normalises one entry. It refuses only what cannot be compared; whether an entry is valid to author is proxy.ValidDomainEntry's question, asked first.
func (Destination) OverlapsAtAnyPort ¶ added in v0.8.9
func (d Destination) OverlapsAtAnyPort(o Destination) bool
OverlapsAtAnyPort reports whether some host the proxy would match against d it would also match against o, whatever the ports. It is the one comparison for both component-host vetoes: a destination a model provider serves must not be reached on any port, and the proxy keys an injected credential by bare host, so a credential on a host collides on every port.
func (Destination) String ¶ added in v0.8.9
func (d Destination) String() string
String is the canonical spelling of d; an entry is in normal form when it equals the String of its own parse.
type Device ¶ added in v0.8.0
type Device struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
// Never serialized: a credential hash has no business leaving the process.
CredentialSHA256 string `json:"-"`
EnrolledBy string `json:"enrolled_by"`
CreatedAt time.Time `json:"created_at"`
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
LastSeq int64 `json:"last_seq"`
LastRowHash string `json:"last_row_hash,omitempty"`
}
Device is one enrolled laptop daemon: an inventory row binding a revocable bearer credential to that device's own audit-federation progress. CredentialSHA256 never reaches a row unhashed; a revoked credential's WHERE RevokedAt IS NULL makes it answer exactly like an unknown one (no oracle). LastSeq/LastRowHash are the chain cursor as ingested so far, verified against each new batch's claimed PrevHash before advancing atomically.
type DeviceEnrolRequest ¶ added in v0.8.0
type DeviceEnrolRequest struct {
Token string `json:"token"`
}
DeviceEnrolRequest/DeviceEnrolResponse are POST /devices/enrol's wire shapes; DeviceAck is the ingest/heartbeat routes' recorded-cursor answer.
type DeviceEnrolResponse ¶ added in v0.8.0
type DeviceEnrolmentToken ¶ added in v0.8.0
type DeviceEnrolmentToken struct {
ID uuid.UUID `json:"id"`
TokenSHA256 string `json:"-"`
DeviceName string `json:"device_name"`
MintedBy string `json:"minted_by"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
ConsumedAt *time.Time `json:"consumed_at,omitempty"`
Token string `json:"token,omitempty"` // plaintext, mint response ONLY
}
DeviceEnrolmentToken is a single-use admin-minted token a laptop's first boot exchanges for a Device credential. ConsumeEnrolmentToken's conditional UPDATE ... RETURNING (WHERE ConsumedAt IS NULL) makes redemption single-use.
type DriveBackend ¶ added in v0.7.0
type DriveBackend string
DriveBackend names WHERE a drive's bytes live and therefore WHICH runner can mount it. Closed and complete; migration 0054's user_drives.backend CHECK is pinned against these constants.
const ( DriveBackendDockerVolume DriveBackend = "docker_volume" // per-user Docker named volume, created on first use. Managed, Docker only. // DriveBackendHostPath binds <host_root>/<home> from a tree the OPERATOR // mounted host-side; Wardyn never holds a share credential. Share, Docker only. DriveBackendHostPath DriveBackend = "host_path" DriveBackendK8sPVC DriveBackend = "k8s_pvc" // per-user dynamically-provisioned PVC. Managed, Kubernetes only. // DriveBackendK8sPVCStatic is an admin-precreated PVC; Wardyn only ever // READS it, a missing claim is a refusal not a create. Share, Kubernetes only. DriveBackendK8sPVCStatic DriveBackend = "k8s_pvc_static" )
func (DriveBackend) Kind ¶ added in v0.7.0
func (b DriveBackend) Kind() DriveKind
Kind derives the managed/share split from the backend. An unknown backend reads as a SHARE — the conservative half, since Wardyn never creates or deletes a share.
func (DriveBackend) RunnerTarget ¶ added in v0.7.0
func (b DriveBackend) RunnerTarget() string
RunnerTarget is the ONE Config.RunnerTarget value that can mount this backend, or "" — lets the write boundary refuse a k8s drive on a Docker deployment (400) rather than store a row that can never resolve.
func (DriveBackend) Valid ¶ added in v0.7.0
func (b DriveBackend) Valid() bool
Valid reports whether b is one of the four backends, mirroring CapabilitySubjectType.Valid.
type DriveKind ¶ added in v0.7.0
type DriveKind string
DriveKind is the managed-vs-share split. DERIVED from the backend and never stored: two columns that must agree are two columns that can disagree.
const ( DriveKindManaged DriveKind = "managed" // Wardyn allocates the object for the member )
type DriveMount ¶ added in v0.7.0
type DriveMount struct {
// DriveID is the user_drives row this mount came from; labels and reclaim
// sweeps group by it.
DriveID uuid.UUID `json:"drive_id"`
Backend DriveBackend `json:"backend"`
ObjectName string `json:"object_name"` // what the substrate is asked for — volume name, PVC name, or absolute host path
// HostRoot is THIS drive's own share root, carried on a host_path mount
// so the driver can assert the bind stays inside it. Does NOT replace the
// deployment's WARDYN_USER_DRIVE_HOST_ROOTS ceiling (that bounds EVERY
// drive at once; this refuses a link into a different drive's tree). The
// driver asserts BOTH.
HostRoot string `json:"host_root,omitempty"`
// DriveName is the drive's human name, carried for the AUDIT row alone so
// a member learns which drive without the operator's absolute host path.
DriveName string `json:"drive_name,omitempty"`
StorageClass string `json:"storage_class,omitempty"` // provisioner a managed k8s_pvc claim asks for; carried since a substrate never reads the database
HomeName string `json:"home_name"` // per-user segment ObjectName was built from
// SubjectHash is the non-PII fingerprint of the principal this mount was
// resolved for. DEFENCE IN DEPTH over the managed-backend collision the
// write boundary refuses: a stored row predating that refusal can still
// resolve two principals onto one volume, so the driver stamps this and
// refuses the second. A digest, never the claim — a label is echoed by
// `docker volume inspect` to anybody who can reach the daemon.
SubjectHash string
Target string `json:"target"` // reserved in-container path (runner.DriveTarget), carried so a runner never hard-codes it
ReadOnly bool `json:"read_only,omitempty"`
SizeMiB int `json:"size_mib,omitempty"`
Enforcement StorageEnforcement `json:"enforcement"`
}
DriveMount is the RESOLVED answer the runner acts on: one principal's drive, folded with grant overrides and narrowed by their run request, in the shape a substrate can execute without re-reading a row.
A SEPARATE type from ResolvedDrive: that one is admin-facing and carries the whole drive/grant rows; this is run-facing and carries only what a mount needs. ReadOnly rather than Writable, since every mount type the substrates speak says ReadOnly. Does NOT ride RunPolicySpec.WorkspaceMounts: a drive is per-PRINCIPAL and a workspace mount is per-ROW.
type DriveObjectScheme ¶ added in v0.8.0
type DriveObjectScheme string
DriveObjectScheme names which half of DriveObjectName's minted form carries the drive — the variable-width slug (migration 0054, `wardyn-drive-<drive-slug>-<home>`) or the drive's own fixed-width id (migration 0067, `wardyn-drive-<drive-id-hex>-<home>`). Closed; pinned against user_drives.object_scheme's CHECK.
IT IS NOT CLIENT-AUTHORED. store.UpsertUserDrive derives it entirely and never reads this field off the request — neither substrate can rename a storage object, so a row keeps whatever scheme it was created under. It rides the wire only so a GET and its round-tripped PUT agree; driveIdentityFields compares it, so a forged flip answers 409.
const ( // DriveObjectSchemeSlug is the original form: DriveSlug at a variable // offset before <home>. The column's DEFAULT; every pre-0067 row reads as // this and stays this permanently (DriveSlug is not injective). DriveObjectSchemeSlug DriveObjectScheme = "slug" // DriveObjectSchemeID is the drive's own UUID, dashless (DriveObjectID), // at a FIXED offset before <home>. Every row created from 0067 onward // gets this unconditionally. DriveObjectSchemeID DriveObjectScheme = "id" )
func (DriveObjectScheme) Valid ¶ added in v0.8.0
func (s DriveObjectScheme) Valid() bool
Valid reports whether s is one of the two schemes.
type DriveReclaim ¶ added in v0.7.0
type DriveReclaim string
DriveReclaim is the DECLARED INTENT for a drive's objects when a grant goes away. v1 executes it by documented operator command, not by code: nothing in the control plane deletes a volume or a PVC.
const ( DriveReclaimRetain DriveReclaim = "retain" // keeps the object after the grant is removed — the default DriveReclaimDelete DriveReclaim = "delete" // records that the object SHOULD be reclaimed — a runbook note, not an action )
func (DriveReclaim) Valid ¶ added in v0.7.0
func (r DriveReclaim) Valid() bool
Valid reports whether r is one of the two reclaim intents.
type EffectiveLimit ¶ added in v0.9.0
type EffectiveLimit struct {
Default LimitAmount `json:"default"`
Cap LimitAmount `json:"cap"`
DefaultSource LimitSource `json:"default_source,omitempty"`
CapSource LimitSource `json:"cap_source,omitempty"`
}
EffectiveLimit is one field's answer: what an untouched run gets, the most a request may ask for, and which source bound each. A source is absent when nothing bounded the value (an unlimited cap, or an unlimited default).
func (EffectiveLimit) Apply ¶ added in v0.9.0
func (l EffectiveLimit) Apply(asked int) (applied LimitAmount, capped bool)
Apply is a request's value against the limit: 0 asks for the default, a value within the cap is kept, and one past it is reduced to the cap with capped set. Reduced, never refused, as a request's CPU and memory are today.
type EffectiveRunLimits ¶ added in v0.9.0
type EffectiveRunLimits struct {
RunType RunnerPoolRunType `json:"run_type"`
CPUMillis EffectiveLimit `json:"cpu_millis"`
MemoryMiB EffectiveLimit `json:"memory_mib"`
// DiskMiB is the main drive's size in MiB. An unlimited Default means an untouched
// run gets no size (its scratch is unbounded); with no default but a cap, an
// untouched run gets the cap (foldField's rule).
DiskMiB EffectiveLimit `json:"disk_mib"`
Lifetime EffectiveLimit `json:"lifetime_sec"`
LifetimeSpan EffectiveLimit `json:"lifetime_span_sec"`
LifetimeLease EffectiveLimit `json:"lifetime_lease_sec"`
NoEndAllowed bool `json:"no_end_allowed"`
Idle *EffectiveLimit `json:"idle_sec,omitempty"`
Concurrency []ConcurrencyBound `json:"concurrency"`
}
EffectiveRunLimits is what a run of one type gets in one pool for one person, as the preview and the launch both read it. Idle is nil for a Background run: it has no idle stop.
Lifetime caps a FINITE end and is the one number a create reads. It is folded from two bounds that an extension must read apart, because they are measured from different moments: LifetimeSpan (the pool, the deployment and the runner) counts from the run's start, and a run past it is KILLED; LifetimeLease (the person's governance) counts from the moment an end is asked for. A new run's end is bounded by the lesser of the two; an extension's by the lesser of start + LifetimeSpan.Cap and now + LifetimeLease.Cap. NoEndAllowed says whether the run may have no end at all, which is a separate yes: every source must allow it, and governance allows it only when its profile does, whatever finite maximum it also sets.
type EgressRedirect ¶ added in v0.5.0
type EgressRedirect struct {
// From is the public/upstream URL or host being redirected away from.
From string `json:"from"`
// To is the corporate-internal URL or host every matching run's egress
// substitutes to; for an Ecosystem row it's also emitted into that
// ecosystem's config file (the old ArtifactOverride.BaseURL).
To string `json:"to"`
// TokenSecretRef optionally names a secret injected proxy-side as a
// Bearer token for To's host (the sandbox never holds it). Mutually
// exclusive with TokenIntegrationRef.
TokenSecretRef string `json:"token_secret_ref,omitempty"`
// TokenIntegrationRef optionally names an Integration
// (SiteConfig.Integrations[i].ID) to take this redirect's token FROM
// instead of a bare secret. The redirect points AT the integration
// rather than restating its secret, inheriting its Header/Format so a
// feed authenticating with something other than "Authorization: Bearer"
// finally can. Naming nothing, a disabled row, or one with no header
// credential degrades to redirect-without-token.
TokenIntegrationRef string `json:"token_integration_ref,omitempty"`
// Ecosystem, when set, is one of six package-manager ecosystems
// ("npm"|"pip"|"cargo"|"maven"|"go"|"nuget") this redirect ALSO emits a
// config file for. Empty means NETWORK-ONLY: no config file is emitted.
Ecosystem string `json:"ecosystem,omitempty"`
}
EgressRedirect is one outbound redirect: requests to From are substituted to To (From's host dropped from egress, To's host allowed), with an optional token injected proxy-side for To's host. From/To share ArtifactOverride.BaseURL's validation discipline — keep any future change there in sync, since workspacescan.EmitArtifactConfig relies on it for raw string interpolation into .npmrc/settings.xml/etc.
type EphemeralProvider ¶ added in v0.7.2
type EphemeralProvider struct {
DefaultDiskMiB int `json:"default_disk_mib,omitempty"` // 0 = unset
// MaxDiskMiB: ceiling a larger request is clamped to (0 = unlimited); a
// DefaultDiskMiB above it is refused at the write boundary.
MaxDiskMiB int `json:"max_disk_mib,omitempty"`
}
EphemeralProvider is the ephemeral-scratch policy: default size, and the ceiling a larger request is clamped to (never refused).
type FederatedAuditEvent ¶ added in v0.8.0
type FederatedAuditEvent struct {
AuditEvent
Seq int64 `json:"seq"`
}
FederatedAuditEvent is one audit row plus Seq, its local table's own sequence for the row. Seq alone is not identity — a table reset can restart it — so a replay is recognised by seq together with RowHash.
type FirstUseMode ¶
type FirstUseMode string
FirstUseMode controls how the egress proxy handles an UNKNOWN domain — one that is neither explicitly allowed nor denied — under an allowlist policy. It widens the legacy first_use_approval boolean into three explicit modes while staying wire-compatible: UnmarshalJSON still accepts the old bool (true => deny_with_review, false => always_deny). Inert under allow-all egress.
const ( // FirstUseAlwaysDeny hard-denies an unknown domain and logs it, without ever // raising it for human approval. (legacy first_use_approval=false) FirstUseAlwaysDeny FirstUseMode = "always_deny" // FirstUseDenyWithReview raises a pending approval and denies the in-flight // request immediately; once a human approves, a later retry passes. The // sandbox connection is never held open. (legacy first_use_approval=true) FirstUseDenyWithReview FirstUseMode = "deny_with_review" // FirstUseWaitForReview raises a pending approval and HOLDS the connection // open until it is approved/denied or the proxy's hold deadline passes — the // request transparently completes if approved in time. On deadline it fails // closed (403) with the approval left pending, degrading to deny_with_review. FirstUseWaitForReview FirstUseMode = "wait_for_review" )
func (FirstUseMode) Normalize ¶
func (m FirstUseMode) Normalize() FirstUseMode
Normalize maps an empty or unrecognised value to always_deny (fail closed, matching the legacy boolean zero value).
func (FirstUseMode) RaisesApproval ¶
func (m FirstUseMode) RaisesApproval() bool
RaisesApproval reports whether an unknown domain is escalated to a human rather than hard-denied (true for both review modes).
func (*FirstUseMode) UnmarshalJSON ¶
func (m *FirstUseMode) UnmarshalJSON(b []byte) error
UnmarshalJSON accepts BOTH the legacy boolean form (true => deny_with_review, false => always_deny) and the new string form, so old stored policies whose first_use_approval is a JSON boolean keep decoding without a migration.
func (FirstUseMode) Valid ¶
func (m FirstUseMode) Valid() bool
Valid reports whether m is empty (unset => default always_deny) or one of the three known modes. Used to reject a hand-authored policy with a garbage value at the API boundary; runtime reads still fail closed via Normalize.
type GitLane ¶ added in v0.7.2
type GitLane string
GitLane is one of the credential lanes a run can clone with. A provider row's Lanes only veto lanes; they never mint or create a new credential.
const ( GitLaneApp GitLane = "app" // GitHub App broker lane (github.com only) GitLanePAT GitLane = "pat" // stored git-pat-<slug> lane GitLaneSSH GitLane = "ssh" // stored ssh-key-<slug> lane GitLaneEntra GitLane = "entra" // Microsoft Entra lane: run authorizes as the person, against the org's own tenant; Azure DevOps hosted only )
type GitPATScope ¶ added in v0.8.6
type GitPATScope struct {
Host string `json:"host"`
SecretName string `json:"secret_name"`
Username string `json:"username,omitempty"`
Repos *[]string `json:"repos,omitempty"`
Access string `json:"access,omitempty"`
API bool `json:"api,omitempty"`
Forge string `json:"forge,omitempty"`
}
GitPATScope is the one decoded shape of a git_pat grant scope. api, broker and composer all read it, so the contract is defined once.
The four narrowing axes have explicit omission defaults (Normalize):
repos absent = every repository the PAT reaches; an EMPTY list = none access write api false forge generic
Repos is a pointer to a slice so "absent" (nil) and "empty" (non-nil, no entries) stay distinct. Reading an empty list as unset would turn an empty intersection into an unnarrowed grant.
Wardyn narrows the run, not the PAT: the credential itself keeps whatever scope the forge issued it with.
func DecodeGitPATScope ¶ added in v0.8.6
func DecodeGitPATScope(raw json.RawMessage) (GitPATScope, error)
DecodeGitPATScope is the lenient read: unknown keys are ignored, so a stored row with a stray key still loads and launches. host and secret_name are required (fail closed). The result is normalized.
func DecodeGitPATScopeStrict ¶ added in v0.8.6
func DecodeGitPATScopeStrict(raw json.RawMessage) (GitPATScope, error)
DecodeGitPATScopeStrict is the write-time decode, for request bodies and the boot policy file. Beyond DecodeGitPATScope it refuses an unknown key (a typo such as "repo" would otherwise read as an omission, and an omission means unnarrowed), an access or forge outside its enum, a malformed repos entry, and api: true on the generic forge, which has no API table. Whether a Bitbucket Server API grant is allowed is a deployment flag, which the api package checks at write.
func (GitPATScope) Narrowed ¶ added in v0.8.6
func (s GitPATScope) Narrowed() bool
Narrowed reports whether the scope narrows the run below the PAT: it sets repos (an empty list included), sets access read, or sets api true. Call it on a normalized scope.
func (GitPATScope) Normalize ¶ added in v0.8.6
func (s GitPATScope) Normalize() GitPATScope
Normalize applies the omission defaults and fails closed on a value outside an enum: an unknown access reads as read, an unknown forge as generic (the strictest path identity). Strict decoding refuses both before they can be stored; this only matters for a row that predates the check. Repos is returned as is, because absent and empty mean different things.
func (GitPATScope) SetsAnyAxis ¶ added in v0.8.6
func (s GitPATScope) SetsAnyAxis() bool
SetsAnyAxis is Narrowed, or a forge other than generic: the test for a scope a lane that ignores the four axes must refuse rather than carry silently.
type GitProvider ¶ added in v0.7.2
type GitProvider struct {
ID string `json:"id"` // operator-chosen stable slug, unique within Git, held to integrationRefRE; a capability grant names a provider by this id
Kind GitProviderKind `json:"kind"` // which forge's bespoke behaviour this row carries
// Disabled turns the row off without deleting it (zero value enabled); a
// disabled row still claims its hosts rather than falling through to ScmHosts.
Disabled bool `json:"disabled,omitempty"`
// BaseURLs are the https addresses this row admits: a repo is admitted
// when its clone URL's scheme/host match one of these and its path equals
// or extends that base URL's path at a "/" boundary. 1-8 entries; no
// port, credentials, query or fragment; at most two path segments.
BaseURLs []string `json:"base_urls"`
// Lanes are the credential lanes usable for this provider; empty means
// every legacy lane (LegacyGitLanes) — the field narrows, never widens.
Lanes []GitLane `json:"lanes,omitempty"`
// GitHubAppInstallURL is operator-configured, nonsecret installation
// metadata for this row's App lane. It grants no access and is disclosed
// by run facts only to operators, never ordinary members.
GitHubAppInstallURL string `json:"github_app_install_url,omitempty"`
CredentialSource CredentialSource `json:"credential_source,omitempty"` // whose credential the lanes use; empty = CredentialSourceShared, CredentialSourcePerUser requires GitLaneEntra, or GitLanePAT on an Azure DevOps row (a Server row, which has no Entra sign-in)
Entra *ADOEntraConfig `json:"entra,omitempty"` // present only on, and required by, a row whose Lanes name GitLaneEntra
}
GitProvider is one git-provider row: a kind, the base URLs it admits, and the credential lanes it permits.
type GitProviderKind ¶ added in v0.7.2
type GitProviderKind string
GitProviderKind is the closed set of git forges Wardyn has bespoke behaviour for; a self-hosted forge is a github (GHES) or azure_devops (ADO Server) row, not a third kind.
const ( GitProviderGitHub GitProviderKind = "github" // github.com or GitHub Enterprise Server GitProviderAzureDevOps GitProviderKind = "azure_devops" // dev.azure.com, org.visualstudio.com, or ADO Server )
func (GitProviderKind) Valid ¶ added in v0.7.2
func (k GitProviderKind) Valid() bool
Valid reports whether k is one of the closed kinds.
type GovernanceAssignment ¶ added in v0.7.0
type GovernanceAssignment struct {
ID uuid.UUID `json:"id"`
SubjectType CapabilitySubjectType `json:"subject_type"`
Subject string `json:"subject"`
ProfileID uuid.UUID `json:"profile_id"`
Priority int `json:"priority"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by,omitempty"`
}
GovernanceAssignment binds one profile to one subject (migration 0052's governance_assignments row).
SubjectType REUSES CapabilitySubjectType — the same vocabulary capability_grants is written against; a second enum for the same thing is the dual-matcher drift this codebase warns against elsewhere.
Priority breaks ties WITHIN a tier (higher wins), the group tier's working lever since a member is often in several groups. Never crosses tiers: a user-tier row beats every group-tier row at any priority.
type GovernanceChange ¶ added in v0.8.6
type GovernanceChange struct {
ID uuid.UUID `json:"id"`
TargetKind string `json:"target_kind"`
Op string `json:"op"`
TargetKey string `json:"target_key"`
State string `json:"state"`
ProposedBy string `json:"proposed_by"`
ProposedAt time.Time `json:"proposed_at"`
ExpiresAt time.Time `json:"expires_at"`
Diff json.RawMessage `json:"diff"`
DecidedBy string `json:"decided_by,omitempty"`
DecidedAt *time.Time `json:"decided_at,omitempty"`
// Reason is a rejection's optional explanation.
Reason string `json:"reason,omitempty"`
// Payload, hashes and emails stay server-side.
Payload json.RawMessage `json:"-"`
BaseHash string `json:"-"`
DeploymentHash string `json:"-"`
ProposedByEmail string `json:"-"`
DecidedByEmail string `json:"-"`
}
GovernanceChange is one governance write held for a second human (migration 0126). Payload is the validated request, replayed on approval; Diff is the server-rendered before/after the reviewer reads. Neither carries a secret value.
The four personal fields (ProposedBy, ProposedByEmail, DecidedBy, DecidedByEmail) are cleared by the audit_personal_fields erasure scope. The emails never reach a response: they exist for the distinct-human comparison and the erasure match only.
type GovernanceLimits ¶ added in v0.7.0
type GovernanceLimits struct {
// LocalSelfDefinedComponents permits a local run to carry a component its launcher defined.
// Omitted or false refuses it; runner-reported confinement is not platform attestation.
LocalSelfDefinedComponents bool `json:"local_self_defined_components,omitempty"`
// DenyTaskModeExec refuses task_mode=exec for a member under this profile.
DenyTaskModeExec bool `json:"deny_task_mode_exec,omitempty"`
// DenyInteractive refuses an interactive run under this profile.
// Evaluated against POST-COERCION interactivity, since omitting the
// task coerces to interactive later — a raw "did the caller ask" check
// would be evaded by leaving the field out. It also refuses the terminal
// attach and SSH into a run created under this profile, since a task or
// exec run still has a sandbox to open a shell in (#1392).
DenyInteractive bool `json:"deny_interactive,omitempty"`
// DenyUIApps strips ui_apps from a run under this profile at create, and
// the UI gateway refuses a session into a run created under it. A limit
// rather than an empty ceiling ui_apps, which stays "no opinion" so
// existing profiles keep their behaviour (#1391).
DenyUIApps bool `json:"deny_ui_apps,omitempty"`
// MaxConcurrentRuns caps how many NON-TERMINAL runs a member under this
// profile may hold at once. 0 is unlimited.
MaxConcurrentRuns int `json:"max_concurrent_runs,omitempty"`
// DenyUserDrive refuses a USER DRIVE mount under this profile, whatever
// an admin allocated. A DOOR, NOT A QUOTA: a drive outlives the run, so
// "may this principal persist anything at all" is the question (403 +
// authz.denied), not "how big".
DenyUserDrive bool `json:"deny_user_drive,omitempty"`
// DenyMemberDrives refuses CREATING a member drive under this profile, on top of
// storage.user_drive.member_create (the org switch). A DOOR with no zero-value
// opinion: false inherits the switch, so an unset profile changes nothing. DenyUserDrive
// also blocks creation (a person who may mount no drive may not make one); this door
// narrows only the creating. Operators are exempt from both doors.
DenyMemberDrives bool `json:"deny_member_drives,omitempty"`
// DenyPlacements refuses a run whose resolved placement is listed (403 placement_denied +
// authz.denied). A DENY LIST: absent means every placement is permitted, including for a
// member with no assigned profile. Values are `remote` and `local`; a profile denying both is
// refused at write, since the people it binds could run nothing.
DenyPlacements []Placement `json:"deny_placements,omitempty"`
// MaxCPUMillis and MaxMemoryMiB cap the sandbox's CPU (millicores) and memory a run under this
// profile may be given, whatever its ceiling's own resources or a member's request say. 0 is
// unlimited. A CLAMP, NOT A DOOR, like MaxEphemeralDiskMiB. Folded into composer.Clamp at
// create and preflight and re-applied at dispatch; assigned members only, operators exempt.
MaxCPUMillis int `json:"max_cpu_millis,omitempty"`
MaxMemoryMiB int `json:"max_memory_mib,omitempty"`
// MaxEphemeralDiskMiB caps the EPHEMERAL scratch (the writable layer when no drive is
// mounted) a run under this profile may be given. 0 is unlimited. A CLAMP, NOT A DOOR: no
// authz.denied at the bound. Enforced at dispatch only (runs_dispatch.go), folded with the
// deployment's own storage.ephemeral.max_disk_mib; assigned members only, operators exempt.
// Whether it actually binds depends on the substrate (Docker's overlay2 doesn't enforce
// size at all) — render through StorageEnforcement, never claim a cap the substrate doesn't keep.
MaxEphemeralDiskMiB int `json:"max_ephemeral_disk_mib,omitempty"`
// MaxDriveSizeMiB caps how large a USER DRIVE may be under this profile. 0 is unlimited —
// DenyUserDrive's twin ("may persist" vs "how much"). PER PRINCIPAL: a drive is one tree
// belonging to one subject; a team-shared drive's ceiling is not the sum of its members'
// and can't be derived from one, so sharing (0.8) adds a scope axis instead. Clamped in
// newResolvedDrive, the one place holding both facts — never at grant write, where the
// subject is claims-resolved and unreadable from the row. Folds with
// storage.user_drive.max_size_mib in one min(), so launch, /me and preview can't disagree.
MaxDriveSizeMiB int `json:"max_drive_size_mib,omitempty"`
// AutonomyRubric maps a run's posture to a permitted AutonomyLevel
// (0.8, #77). A POINTER so an unset rubric doesn't put
// `"autonomy_rubric":{}` on every profile's wire body. Nil means "no
// rubric" (resolveRunAutonomy, #97, treats it like no assigned profile).
AutonomyRubric *AutonomyRubric `json:"autonomy_rubric,omitempty"`
// RunLimits is embedded, so its seven fields sit flat on the limits wire
// object beside the ones above — the same field set a run captures at
// create (AgentRun.RunLimits).
RunLimits
}
GovernanceLimits carries the autonomy switches that BOUND A REQUEST rather than a policy (RunPolicySpec bounds what a sandbox may reach once running, enforced by the proxy). These refuse a run SHAPE before it exists, or (DenyUserDrive) leave state behind:
- DenyTaskModeExec: task_mode=exec has no agent and no toolgate, so no tool_rules ceiling binds it.
- DenyInteractive: an interactive run refuses tool_approvals=hold by design, so tool_rules can't express "supervised" there either.
- DenyUIApps: a UI app is relay access into the sandbox that no tool_rules ceiling sees.
- DenyUserDrive: a drive OUTLIVES the run, so no tool_rules ceiling describes the escape — the storage itself is the escape.
- DenyPlacements: a placement the people under this profile may not run on; a deny list, so absent means permitted.
- MaxConcurrentRuns is a QUOTA, not a door: 422 with no authz.denied, not 403.
- MaxCPUMillis/MaxMemoryMiB/MaxEphemeralDiskMiB/MaxDriveSizeMiB are neither: they CLAMP — capped and told so, never refused (disk_mib is policy-authored, so refusing would break every stored policy the day a limit is first written).
A CLOSED struct with `omitempty` on every field, not a map: small, complete, validated by the Go type itself, no DB CHECK needed. Every zero value means "unrestricted" except LocalSelfDefinedComponents (default deny), so an omitted limits object behaves exactly as before this struct existed.
type GovernanceProfile ¶ added in v0.7.0
type GovernanceProfile struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Ceiling RunPolicySpec `json:"ceiling"`
Limits GovernanceLimits `json:"limits"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by,omitempty"`
// Contact is who owns the profile and how to ask for a change; nil is none.
// Validated on write and re-validated on every read (internal/policyref).
// Never inherited from a base: disclosing a base's contact would disclose the base.
Contact *policyref.Contact `json:"contact,omitempty"`
// ContactSet is a write instruction, never stored or serialized: true makes an
// upsert write Contact (nil clears it), false keeps the stored value.
ContactSet bool `json:"-"`
// BaseProfileID is the profile this one composes on; nil with a non-nil Overlay means the
// deployment default. Only a composed profile has one.
BaseProfileID *uuid.UUID `json:"base_profile_id,omitempty"`
// Overlay marks the profile composed. A non-nil pointer to an empty overlay is valid: it is the
// base unchanged.
Overlay *CeilingOverlay `json:"overlay,omitempty"`
// OverlayLimits narrows the base's limits. Only with an Overlay; nil is no narrowing.
OverlayLimits *LimitsOverlay `json:"overlay_limits,omitempty"`
}
GovernanceProfile is one named, assignable ceiling (the governance_profiles row of migrations 0052 and 0125).
A STANDALONE profile (Overlay nil) is a full RunPolicySpec with replacement semantics: an assigned profile IS the principal's ceiling; with no assignment a principal falls through to Config.DefaultPolicy byte for byte. A COMPOSED profile (Overlay set) stores no raw Ceiling or Limits, only a base (BaseProfileID, or the deployment default when nil) and an overlay that can only narrow it; the effective ceiling is composer.ApplyOverlay over the resolved base, computed whenever authority is read (internal/api/governance_compose.go is the only reader). composer.Clamp is NOT that meet (it drops workspace_mounts unconditionally, is order-dependent through llm_inspection and defaults unnamed tools to hold), so composition has its own function.
Name is the UNIQUE human handle: what an admin assigns by, what the console lists, and the resolver's last ORDER BY tie-break.
func (GovernanceProfile) Composed ¶ added in v0.8.6
func (p GovernanceProfile) Composed() bool
Composed reports whether the profile is a base plus an overlay rather than a standalone ceiling.
type GrantDelivery ¶ added in v0.9.0
type GrantDelivery string
GrantDelivery is how one grant reached a run (credential_grants.delivery).
const ( GrantDeliveryNone GrantDelivery = "" GrantDeliveryOwn GrantDelivery = "own" GrantDeliveryViaOrg GrantDelivery = "via_org" GrantDeliveryResident GrantDelivery = "runner_resident" )
type GrantKind ¶
type GrantKind string
GrantKind enumerates broker-mintable credential kinds.
const ( GrantGitHubToken GrantKind = "github_token" GrantCloudSTS GrantKind = "cloud_sts" // HARD-REQUIRES SPIRE identity provider GrantAPIKey GrantKind = "api_key" // proxy-side injection only // GrantGitPAT returns a stored PAT to the git credential helper for a non-GitHub host (ADO/GitLab), an opaque CONNECT tunnel the proxy can't inject into. GrantGitPAT GrantKind = "git_pat" // GrantSSHKey materializes a resident, agent-readable SSH private key for git-over-SSH — a DOCUMENTED EXCEPTION to the no-resident-secret invariant // (no credential-helper seam for SSH). agent-run writes it 0400 just before the clone and wipes it right after; residual risk is code running as the // agent uid during that window. See broker.mintSSHKey. GrantSSHKey GrantKind = "ssh_key" // GrantEnvSecret places a stored secret into the sandbox environment at dispatch — the second DOCUMENTED EXCEPTION to the no-resident-secret invariant. // Unlike every other kind: NOT BROKERED (no mint/approval/TTL/JTI); RESIDENT FOR THE WHOLE RUN (readable via /proc/self/environ as the agent uid); // NO REVOCATION (killing the run doesn't un-disclose it). ADMIN-ONLY by default — see threatmodel/THREAT-MODEL.md §5.1a. GrantEnvSecret GrantKind = "env_secret" // GrantFileSecret places a stored secret in a FILE inside the sandbox at dispatch — the third DOCUMENTED EXCEPTION to the no-resident-secret invariant. // Like GrantEnvSecret: NOT BROKERED (no mint/approval/TTL/JTI), RESIDENT FOR THE WHOLE RUN, NO REVOCATION (killing the run doesn't un-disclose it). // The path is fixed, runner.ComponentSecretDir plus the scope's file name, never authored. ADMIN-ONLY by default for a policy author, under the same // switch as GrantEnvSecret; a person's OWN secret reaches a run this way through a custom component, under the component permission. GrantFileSecret GrantKind = "file_secret" )
type GrantSpec ¶
type GrantSpec struct {
Kind GrantKind `json:"kind"`
// Scope is kind-specific: {repos,permissions} for github_token; {host,header} for api_key;
// {host,secret_name,username?} for git_pat; {host,key_secret_ref,username?,known_hosts_secret_ref?} for ssh_key;
// {name,secret_name} for env_secret; {file,secret_name} for file_secret.
Scope json.RawMessage `json:"scope"`
// TTL of the minted credential. Max (and default) 1h.
TTLSeconds int `json:"ttl_seconds,omitempty"`
// RequiresApproval forces a human approval to mint (vs auto-mint on policy).
RequiresApproval bool `json:"requires_approval"`
OwnerOnly bool `json:"owner_only,omitempty"` // stored secret from the run owner's own row only, never the operator's
}
GrantSpec is a credential scope description. The broker enforces the invariant: a minted credential's scope is exactly the approved scope — never wider.
type HarnessCredential ¶ added in v0.9.0
type HarnessCredential struct {
Harness string `json:"harness"`
// SHA256 is the lower-case hex digest of the credential.
SHA256 string `json:"sha256"`
}
HarnessCredential binds one included harness to the SHA-256 of the random credential minted for it at dispatch. The proxy config carries only the digest; the plaintext goes to that harness's own process (WARDYN_TOOLGATE_CREDENTIAL) and nowhere else, so the approval route can name the harness from a value only that harness was given, never from the request body.
type HarnessToolRules ¶ added in v0.9.0
type HarnessToolRules struct {
Harness string `json:"harness"`
// Default is the effect for a tool no rule names. Empty means the harness
// has no default, and an unmatched tool is held for a human.
Default ToolEffect `json:"default,omitempty"`
Rules []ToolRule `json:"rules,omitempty"`
}
HarnessToolRules is the tool rules of one included harness and its own default effect (the "*" rule). The approval broker's lookup key is Harness: the id of the included tool that raised the request, which for a harness is its agent name.
The harness id MUST come from an identity the proxy binds itself, never from the body of the approval request: every process in the sandbox can post to the proxy's local approvals route, so a harness read from the body lets one tool borrow a sibling's allow. The binding is a per-included-tool local credential minted at dispatch and injected only into that harness's own hook configuration; A-L3 lands it. Until it does, the run door keeps refusing more than one included tool and any per-tool rule or default effect (runModeUnavailable), so no run has a second harness to confuse.
Like PushRuleSet, the sets are the product after the ceiling: policy and ceiling rules are laid into each harness's set first, and a request's per-tool rules may only narrow them (hold or deny; a rule that allows is refused), so an administrator's deny survives a person's allow.
type HomeTemplate ¶ added in v0.7.0
type HomeTemplate string
HomeTemplate names WHICH identity a drive's per-user home name is derived from. Closed; pinned against user_drives.home_template's CHECK.
const ( // HomeTemplateHash is `d-` + a truncated sha256 over the drive id and the // subject: deterministic, DNS-1123-safe, leaks no identity. The default, // and the ONLY template a MANAGED backend may use — ValidateUserDrive // refuses the others on managed. HomeTemplateHash HomeTemplate = "hash" // HomeTemplateSub uses the sign-in subject claim verbatim, lowercased. // SHARE backends only: two subjects differing only in case collapse to // one home. HomeTemplateSub HomeTemplate = "sub" // HomeTemplateEmailLocal uses the part of the email claim before the "@" // (alice@corp -> alice). SHARE backends only: the domain is dropped, so // alice@corp.example and alice@partner.example name ONE directory — the // answer is a home_override on the colliding person's allocation. // // THERE IS DELIBERATELY NO WHOLE-EMAIL TEMPLATE: driveHomeSegmentRe // excludes "@", so it could only ever resolve for a claim that was not an // address; anything else is a per-user home_override. HomeTemplateEmailLocal HomeTemplate = "email_local" )
func (HomeTemplate) Valid ¶ added in v0.7.0
func (t HomeTemplate) Valid() bool
Valid reports whether t is one of the three templates.
type ImageBuild ¶ added in v0.9.0
type ImageBuild struct {
Schema int `json:"schema"`
Lane string `json:"lane"`
CacheKey string `json:"cache_key"`
BuildID string `json:"build_id"`
Reason string `json:"reason"`
BuiltAt time.Time `json:"built_at"`
WardynVersion string `json:"wardyn_version"`
Resolved bool `json:"resolved"`
Inputs ImageBuildInputs `json:"inputs"`
Image ImageBuildImage `json:"image"`
}
ImageBuild is the record of what built a workspace's image. An empty string means "not known"; a field is never guessed.
func (ImageBuild) SHA256 ¶ added in v0.9.0
func (r ImageBuild) SHA256() (string, error)
SHA256 is the hex SHA-256 of the record's encoding/json bytes. It is computed, never stored.
type ImageBuildFeature ¶ added in v0.9.0
type ImageBuildFeature struct {
Ref string `json:"ref"`
Digest string `json:"digest"`
Options map[string]any `json:"options"`
}
ImageBuildFeature is a dev container feature; Options is what the generator wrote.
type ImageBuildGenerator ¶ added in v0.9.0
type ImageBuildGenerator struct {
Salt string `json:"salt"`
FilesSHA256 map[string]string `json:"files_sha256"`
}
ImageBuildGenerator names the generator salt and the hash of each file it built.
type ImageBuildImage ¶ added in v0.9.0
ImageBuildImage is the build's own tag and its image ID.
type ImageBuildInputs ¶ added in v0.9.0
type ImageBuildInputs struct {
Base ImageBuildRef `json:"base"`
Features []ImageBuildFeature `json:"features,omitempty"`
Installers []ImageBuildInstaller `json:"installers,omitempty"`
Generator *ImageBuildGenerator `json:"generator,omitempty"`
Repo *ImageBuildRepo `json:"repo,omitempty"`
}
ImageBuildInputs are the build's unpinned-or-pinned inputs, per lane.
type ImageBuildInstaller ¶ added in v0.9.0
type ImageBuildInstaller struct {
Tool string `json:"tool"`
Channel string `json:"channel"`
Version string `json:"version"`
Platform string `json:"platform"`
SHA256 string `json:"sha256"`
}
ImageBuildInstaller is a tool the generated Dockerfile installs.
type ImageBuildRef ¶ added in v0.9.0
ImageBuildRef is an image reference and the digest it resolved to.
type ImageBuildRepo ¶ added in v0.9.0
ImageBuildRepo is the repository a repo-devcontainer build read, as built.
type Integration ¶ added in v0.5.0
type Integration struct {
// ID is an operator-chosen stable slug, unique within
// SiteConfig.Integrations (no generated uuid — egress redirects'
// token_integration_ref and workspace requirements point at it).
ID string `json:"id"`
Name string `json:"name"`
// Kind is the ONE field saying what this connects to: a closed-set kind
// (ClosedIntegrationKinds) with bespoke behavior, or any other slug (generic).
Kind string `json:"kind"`
// Disabled turns the integration off without deleting its configuration.
Disabled bool `json:"disabled,omitempty"`
// Secrets are the required secrets, each with its delivery; refs/names only.
Secrets []IntegrationSecret `json:"secrets,omitempty"`
// Egress is WHERE THE SYSTEM LIVES: hosts this integration may reach
// (proxy.ValidDomainEntry syntax: exact host or leading-"*." wildcard,
// optionally ":port"). TRUST BOUNDARY: a wildcard opens the path but
// never carries the credential — proxy-side injection needs an exact
// entry, so a proxy_header secret is rejected at write time if any
// egress entry is a wildcard or has a port.
Egress []string `json:"egress,omitempty"`
// Config is non-secret, kind-validated configuration; closed kinds accept
// only known keys (unknown 400s by name at write), generic kinds take any
// string keys.
Config map[string]any `json:"config,omitempty"`
Docs string `json:"docs,omitempty"`
// DisabledCapabilities lists capability names this integration does NOT support.
DisabledCapabilities []string `json:"disabled_capabilities,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// contains filtered or unexported fields
}
Integration is one operator-configured external connection: a base component (required secrets each with its delivery, required egress, non-secret config) extended by kind. Secrets holds names, never values; the broker/proxy resolve the named secret at dispatch time. UnmarshalJSON also accepts the pre-base-component shape, folding it forward one-way (write-new; see foldLegacyIntegration).
func (Integration) CredentialsMap ¶ added in v0.5.0
func (in Integration) CredentialsMap() map[string]string
CredentialsMap flattens Secrets into a role → secret_name map for the ONE consumer that wants this shape: the integration.delete audit payload.
func (Integration) HeaderSecret ¶ added in v0.5.0
func (in Integration) HeaderSecret() (secretName, header, format string, ok bool)
HeaderSecret returns the first proxy_header-delivered secret row as the (secretName, header, format) triple injection consumes. An empty stored format reads as "%s", not injectionRuleFromScope's "Bearer %s" default.
func (Integration) RoleSecret ¶ added in v0.5.0
func (in Integration) RoleSecret(role string) string
RoleSecret returns the secret NAME stored for role ("" when not configured).
func (*Integration) UnmarshalJSON ¶ added in v0.5.0
func (in *Integration) UnmarshalJSON(b []byte) error
UnmarshalJSON accepts BOTH the base-component shape (discriminated by its "kind" key) and the legacy {category, type, hosts, header, credentials} shape, folding the latter forward (foldLegacyIntegration) — the ONE read-time migration chokepoint; marshal emits only the new shape.
type IntegrationDelivery ¶ added in v0.5.0
type IntegrationDelivery struct {
Mode string `json:"mode"` // proxy_header | resident_file | resident_env
// Header/Format apply to proxy_header: field name and value template (one
// %s; empty means the raw secret IS the value).
Header string `json:"header,omitempty"`
Format string `json:"format,omitempty"`
// Path applies to resident_file: the in-sandbox path the secret lands at.
Path string `json:"path,omitempty"`
// Var applies to resident_env: the environment variable name.
Var string `json:"var,omitempty"`
}
IntegrationDelivery says how ONE secret reaches the run; the mode decides which other fields apply.
type IntegrationList ¶ added in v0.5.0
type IntegrationList []Integration
IntegrationList is SiteConfig's integrations slice with the read-time migration applied at decode: rows folded from legacy artifact_mirror/ host_proxy categories are DROPPED (network topology, homed under Corporate network). A new-shape row is never dropped, whatever its kind.
func (*IntegrationList) UnmarshalJSON ¶ added in v0.5.0
func (l *IntegrationList) UnmarshalJSON(b []byte) error
UnmarshalJSON decodes the rows, then drops legacy topology rows.
type IntegrationSecret ¶ added in v0.5.0
type IntegrationSecret struct {
Role string `json:"role"`
SecretName string `json:"secret_name"`
Delivery *IntegrationDelivery `json:"delivery,omitempty"`
}
IntegrationSecret is one required secret of an integration: a role, a ref into the secret store (a NAME, never a value), and how it's delivered. A nil Delivery is allowed on CLOSED kinds only (their own bespoke transport delivers it).
type InternalHost ¶ added in v0.7.0
type InternalHost struct {
// HostSuffix matches a request host by label suffix: itself, or any host
// ending in "."+HostSuffix (never a mid-label substring match).
HostSuffix string `json:"host_suffix"`
// CIDRs scopes the lift to these ranges only, each inside RFC1918,
// fc00::/7, or 100.64.0.0/10 (ipguard.Liftable) — never loopback/
// link-local/metadata/multicast/NAT64. Empty lifts the full Liftable set.
CIDRs []string `json:"cidrs,omitempty"`
// Baseline grades this host and EVERY subdomain as baseline egress, where
// SiteEgress.BaselineHosts names exact hosts. Default false, so declaring an
// internal host never changes a grade unless the operator says so. Set only
// through PUT /governance/egress-baseline (or the boot seed on an empty
// document): PUT /site-config refuses a changed set of marks.
Baseline bool `json:"baseline,omitempty"`
}
InternalHost is one SiteConfig.InternalHosts entry — see that field's doc.
type Invitation ¶ added in v0.9.0
type Invitation struct {
ID uuid.UUID `json:"id"`
Target string `json:"target"`
CreatedBy string `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
TokenName string `json:"token_name,omitempty"`
TokenTTLSeconds *int64 `json:"token_ttl_seconds,omitempty"` // nil = the deployment rule
ReplacesTokenID *uuid.UUID `json:"replaces_token_id,omitempty"`
ClaimedAt *time.Time `json:"claimed_at,omitempty"`
TokenID *uuid.UUID `json:"token_id,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
RevokedBy string `json:"revoked_by,omitempty"`
State InvitationState `json:"state"`
ConsolePath string `json:"console_path,omitempty"` // create response only
}
Invitation lets one named person mint their own API token (migration 0157, #1508). It holds no secret: only the target, signed in, can claim it, and TokenID is metadata that authenticates nothing.
func (Invitation) StateAt ¶ added in v0.9.0
func (i Invitation) StateAt(now time.Time) InvitationState
StateAt is the invitation's state at now: claimed and revoked are final, then the clock decides.
type InvitationState ¶ added in v0.9.0
type InvitationState string
InvitationState is where an invitation stands. It is derived from the row and the clock, never stored.
const ( InvitationPending InvitationState = "pending" InvitationClaimed InvitationState = "claimed" InvitationExpired InvitationState = "expired" InvitationRevoked InvitationState = "revoked" )
type LLMInspectionSpec ¶
type LLMInspectionSpec struct {
// Mode is "off" (default), "alert" (scan + audit, forward unchanged), or
// "block" (a qualifying finding refuses the request). "" == "off".
Mode string `json:"mode"`
// WorkspaceSecretNames are operator-declared secret NAMES, resolved against the at-rest secret
// store — the field an operator/admin actually AUTHORS. A name is not sensitive the way a value
// is, so it may freely appear in a stored policy row. Resolved to WorkspaceSecretValues ONLY at
// dispatch, in memory, on the per-dispatch policy copy handed to the proxy.
WorkspaceSecretNames []string `json:"workspace_secret_names,omitempty"`
// WorkspaceSecretValues are the RESOLVED secret VALUES the run must not leak into a prompt — the
// v1 detection corpus the proxy actually matches against. NOT an authoring field:
// validatePolicySpec refuses a non-empty value here on every policy write. Populated ONLY by
// dispatch, in memory; every other copy carries names only, values redacted to a count. NEVER
// logged. Values shorter than the masking floor are ignored.
WorkspaceSecretValues []string `json:"workspace_secret_values,omitempty"`
// DetectSecrets enables the known-secret detector (exact match against WorkspaceSecretValues).
// At least one Detect* must be true when Mode != off.
DetectSecrets bool `json:"detect_secrets,omitempty"`
// DetectSecretPatterns enables the regex catalog of well-known secret formats (AWS/GitHub/Slack/
// Google keys, PEM keys, JWTs, Stripe) — higher precision than entropy but can false-positive on
// example/test keys in code.
DetectSecretPatterns bool `json:"detect_secret_patterns,omitempty"`
// DetectEntropy enables the Shannon-entropy detector (high FP in code; medium
// severity so a strict block_min_severity can exclude it). Off by default.
DetectEntropy bool `json:"detect_entropy,omitempty"`
// DetectPII enables the regex/Luhn PII detector (best-effort, high false-negative recall — a
// visibility signal, never a control). Off by default.
DetectPII bool `json:"detect_pii,omitempty"`
// DetectorSidecarURL, when set, adds an out-of-process detection sidecar the proxy POSTs each
// span to. Trusted operator config. A sidecar error/timeout/non-200 respects on_scanner_error
// like any other scanner error: fail-OPEN by default, fail-CLOSED when on_scanner_error=block —
// so block mode with the sidecar as the SOLE detector DOES guarantee a block on sidecar failure.
DetectorSidecarURL string `json:"detector_sidecar_url,omitempty"`
// ClassifiedMarkers are operator-defined literal markers (e.g. "INTERNAL ONLY",
// "CONFIDENTIAL//NOFORN") whose presence in outbound content flags a
// classified-content leak. Case-insensitive substring match; category "classified".
ClassifiedMarkers []string `json:"classified_markers,omitempty"`
// ScanAttachments opts into decoding+scanning base64 image/document attachment
// bytes in a prompt (off by default — binary, large, high-FP).
ScanAttachments bool `json:"scan_attachments,omitempty"`
// InspectForwardEgress extends inspection from the LLM routes to the GENERIC plaintext-HTTP
// forward path, so a custom (non-LLM) HTTP connector's POST/PUT body is scanned too. Off by
// default. (HTTPS connectors tunnel via opaque CONNECT and remain uninspected unless MITM'd.)
InspectForwardEgress bool `json:"inspect_forward_egress,omitempty"`
// MaxScanBytes caps the size of a single extracted span scanned; 0 => a
// built-in default. A larger span is skipped (fail-open) and recorded.
MaxScanBytes int `json:"max_scan_bytes,omitempty"`
// OnScannerError selects behavior when the scanner ERRORS (e.g. an
// unparseable body): "pass" (default, fail-open) or "block".
OnScannerError string `json:"on_scanner_error,omitempty"`
// RequireInspectableLLM, when true, refuses to schedule a run whose resolved LLM transport is
// opaque (subscription-OAuth/Bedrock CONNECT) and therefore cannot be inspected — fail-closed,
// like MinConfinementClass. Default false only WARNS (the common subscription user isn't punished).
RequireInspectableLLM bool `json:"require_inspectable_llm,omitempty"`
// InterceptTLS opts the run into TLS-MITM of opaque CONNECT tunnels to known LLM hosts
// (Anthropic/OpenAI), making the subscription-OAuth path inspectable. TRUST BOUNDARY: the
// control plane provisions a per-run CA — the PRIVATE key goes only to the proxy sidecar; the
// sandbox trusts only the CA's PUBLIC cert. Adds a CA trust dependency inside the sandbox. Off by default.
InterceptTLS bool `json:"intercept_tls,omitempty"`
// BlockMinSeverity is the minimum finding severity that triggers a block in
// "block" mode ("low"|"medium"|"high"|"critical"; "" => low).
BlockMinSeverity string `json:"block_min_severity,omitempty"`
}
LLMInspectionSpec configures optional outbound LLM prompt inspection for a run. The zero value (or a nil *LLMInspectionSpec on the policy) means OFF.
type LaunchPreset ¶ added in v0.8.0
type LaunchPreset struct {
Name string `json:"name"`
Version int `json:"version"`
Description string `json:"description,omitempty"`
UserTypes []string `json:"user_types"`
Request json.RawMessage `json:"request"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by,omitempty"`
UpdatedBy string `json:"updated_by,omitempty"`
}
LaunchPreset is one row of launch_presets (migration 0087): an admin-managed, named bundle of existing POST /runs fields. Request is the stored create-run body, kept raw here because its Go type lives in the public SDK (pkg/client.CreateRunRequest), which imports this package.
type LimitAmount ¶ added in v0.9.0
LimitAmount is an effective number. Unlimited is its own value, never a zero.
type LimitSource ¶ added in v0.9.0
type LimitSource string
LimitSource names which bound an effective limit came from.
const ( LimitSourcePool LimitSource = "pool" LimitSourceGovernance LimitSource = "governance" LimitSourceDeployment LimitSource = "deployment" LimitSourceRunner LimitSource = "runner" )
type LimitsOverlay ¶ added in v0.8.6
type LimitsOverlay struct {
LocalSelfDefinedComponents *bool `json:"local_self_defined_components,omitempty"`
DenyTaskModeExec *bool `json:"deny_task_mode_exec,omitempty"`
DenyInteractive *bool `json:"deny_interactive,omitempty"`
DenyUIApps *bool `json:"deny_ui_apps,omitempty"`
MaxConcurrentRuns *int `json:"max_concurrent_runs,omitempty"`
DenyUserDrive *bool `json:"deny_user_drive,omitempty"`
DenyMemberDrives *bool `json:"deny_member_drives,omitempty"`
DenyPlacements *[]Placement `json:"deny_placements,omitempty"`
MaxCPUMillis *int `json:"max_cpu_millis,omitempty"`
MaxMemoryMiB *int `json:"max_memory_mib,omitempty"`
MaxEphemeralDiskMiB *int `json:"max_ephemeral_disk_mib,omitempty"`
MaxDriveSizeMiB *int `json:"max_drive_size_mib,omitempty"`
AutonomyRubric *AutonomyRubric `json:"autonomy_rubric,omitempty"`
MaxEndAheadSec *int `json:"max_end_ahead_sec,omitempty"`
DefaultEndSec *int `json:"default_end_sec,omitempty"`
AllowNoEnd *bool `json:"allow_no_end,omitempty"`
MaxWaitSec *int `json:"max_wait_sec,omitempty"`
DefaultWaitSec *int `json:"default_wait_sec,omitempty"`
UserChangesLimits *bool `json:"user_changes_limits,omitempty"`
PauseIdleAfterSec *int `json:"pause_idle_after_sec,omitempty"`
}
LimitsOverlay is CeilingOverlay's counterpart for GovernanceLimits. The embedded RunLimits fields sit flat on the wire, as they do on the limits object itself.
func DecodeLimitsOverlay ¶ added in v0.8.6
func DecodeLimitsOverlay(raw []byte) (LimitsOverlay, error)
DecodeLimitsOverlay strictly decodes a stored or submitted limits overlay.
type LostReason ¶ added in v0.8.0
type LostReason string
LostReason says why a kept run lost its sandbox (AgentRun.LostReason).
const ( // LostEnded is a run whose lease ran out (AgentRun.EndsAt passed): stopped // and kept for the ended-run grace. LostEnded LostReason = "ended" // LostReboot is an interactive run whose agent container exited under it // (a host reboot, a Docker Desktop restart, a long suspend) but still // exists: kept with its files and its proxy stopped. LostReboot LostReason = "reboot" // LostOutage is an interactive run whose run token lapsed because the // control plane was unreachable past the token's life: its proxy is stopped // so it has no egress, and its agent is left running. LostOutage LostReason = "outage" )
type MemberDrivePolicy ¶ added in v0.9.0
type MemberDrivePolicy struct {
Enabled bool `json:"enabled,omitempty"`
MaxPerPerson int `json:"max_per_person,omitempty"` // 1..MaxMemberDrivesPerPerson when Enabled
// SizeMiB is the k8s claim request, required when Enabled and bounded by
// UserDriveProvider.MaxSizeMiB. A Docker volume has no size limit, so it must be 0 there.
SizeMiB int `json:"size_mib,omitempty"`
StorageClass string `json:"storage_class,omitempty"` // k8s only; "" = the cluster default
}
MemberDrivePolicy turns member drive creation on for the organisation (off by default) and bounds it. A profile can still deny it (GovernanceLimits.DenyMemberDrives).
type ModelProvider ¶ added in v0.8.0
type ModelProvider struct {
ID string `json:"id"` // admin's slug ("corp-gateway"); unique within the block
// UID is SERVER-OWNED: minted on first write, never reissued once deleted. Every person's credential is
// keyed by it, so a submitted value is ignored rather than trusted.
UID string `json:"uid,omitempty"`
Name string `json:"name,omitempty"` // shown when people choose it ("Corp gateway")
Kind ModelProviderKind `json:"kind"`
// Disabled is negative-sense so the zero value is ENABLED. A disabled provider may remain an agent's
// default: that default becomes a refused sentinel, never a fall-through to another provider.
Disabled bool `json:"disabled,omitempty"`
// BaseURL: required for custom_endpoint, an optional gateway override for Anthropic/OpenAI, never set
// on Bedrock (its address is Bedrock.BaseURL).
BaseURL string `json:"base_url,omitempty"`
Auth *ProviderAuth `json:"auth,omitempty"` // how each person's token is sent; custom_endpoint only
Bedrock *BedrockSettings `json:"bedrock,omitempty"` // region, address and sign-in setup for a Bedrock kind
Azure *AzureSettings `json:"azure,omitempty"` // endpoint and route for azure_foundry
// Harnesses are the harnesses this provider serves, with per-pairing settings.
Harnesses []ProviderHarness `json:"harnesses,omitempty"`
}
ModelProvider is one admin-configured provider.
func (ModelProvider) Serves ¶ added in v0.8.0
func (p ModelProvider) Serves(harness string) bool
Serves reports whether harness is enabled for this provider; says nothing about Disabled.
type ModelProviderKind ¶ added in v0.8.0
type ModelProviderKind string
ModelProviderKind is what kind of credential each person brings to a provider, and so which dispatch lane serves it. A closed set.
const ( ModelProviderAnthropicSubscription ModelProviderKind = "anthropic_subscription" // own Claude subscription, via container sign-in ModelProviderBedrockSSO ModelProviderKind = "bedrock_sso" // own AWS sign-in via admin's start URL + account/role pin ModelProviderAnthropicAPIKey ModelProviderKind = "anthropic_api_key" // own Anthropic API key ModelProviderOpenAIAPIKey ModelProviderKind = "openai_api_key" // own OpenAI API key ModelProviderBedrockBearer ModelProviderKind = "bedrock_bearer" // own Bedrock API key // ModelProviderCustomEndpoint: admin's own endpoint, reached with each person's own token/PAT. ModelProviderCustomEndpoint ModelProviderKind = "custom_endpoint" // ModelProviderAzureFoundry: each person's own Entra sign-in, captured for one Azure resource audience. // Writable only while validateModelProviders' activation flag (azureFoundryGateReady) is true. ModelProviderAzureFoundry ModelProviderKind = "azure_foundry" )
func (ModelProviderKind) IsBedrock ¶ added in v0.8.0
func (k ModelProviderKind) IsBedrock() bool
IsBedrock reports whether k is one of the two Bedrock kinds (address/sign-in live in ModelProvider.Bedrock).
func (ModelProviderKind) Valid ¶ added in v0.8.0
func (k ModelProviderKind) Valid() bool
Valid reports whether k is one of the closed kinds.
type ModelProviders ¶ added in v0.8.0
type ModelProviders struct {
Providers []ModelProvider `json:"providers,omitempty"`
}
ModelProviders is the org's model-provider CONFIGURATION: which credential kinds this deployment supports, where each sends requests, and which harnesses may use it. Configuration ONLY — no credential lives on a record, every person supplies their own. The zero value is load-bearing: with no block, run create and dispatch keep the existing lane-resolution path unchanged. Validation lives in internal/api.
func (*ModelProviders) Empty ¶ added in v0.8.0
func (p *ModelProviders) Empty() bool
Empty reports whether p carries no provider — the shape a PUT uses to CLEAR the block, normalized back to nil so a later GET doesn't render "model_providers":{} forever.
type ModelProvidersRead ¶ added in v0.9.0
type ModelProvidersRead struct {
ModelProviders
ConnectedPeople map[string]int `json:"connected_people,omitempty"`
EntraLoginConfigured bool `json:"entra_login_configured"`
KindsAvailable []ModelProviderKind `json:"kinds_available"`
}
ModelProvidersRead is GET /api/v1/model-providers' body: the stored block, and ConnectedPeople — per provider ID, how many people hold a credential of their own for it (#970). Read-only: PUT's strict decode refuses the field, and the ETag covers the stored block alone, so a person connecting never stales an admin's edit. Admins only, as the route is: a member never reads this list. It carries counts and configuration, never a credential value.
EntraLoginConfigured says whether this install has the console Entra login an azure_foundry provider signs people in through; KindsAvailable lists the kinds a write accepts now (azure_foundry only while the release has it switched on). Both are server facts, not stored and not in the ETag.
type OverrideDirection ¶ added in v0.9.0
type OverrideDirection string
OverrideDirection says whether the operation gives the run less reach or more.
const ( OverrideTighten OverrideDirection = "tighten" OverrideWiden OverrideDirection = "widen" )
type OverrideKind ¶ added in v0.9.0
type OverrideKind string
OverrideKind names one thing a person may change on one component for one run (OD-1: New Run card edits are per-component overrides on the request).
const ( OverrideAgentHost OverrideKind = "agent_host" // a destination on the agent component OverrideAgentHostWildcard OverrideKind = "agent_host_wildcard" // the same, spelled with a wildcard OverrideAgentSecret OverrideKind = "agent_secret" // a stored secret injected as a header on the agent component OverrideToolRuleRestrict OverrideKind = "tool_rule_restrict" // a tool rule whose effect is hold or deny OverrideToolRuleAllow OverrideKind = "tool_rule_allow" // a tool rule whose effect is allow OverrideADOCapability OverrideKind = "ado_capability" // the run's Azure DevOps capability set OverrideGitPATScope OverrideKind = "git_pat_scope" // repos, access or api of a git_pat grant OverridePushDeny OverrideKind = "push_rule_deny" // a push deny path of one provider and organisation OverridePushReview OverrideKind = "push_rule_review" // a push require-review path of one provider and organisation OverrideRunHostAllow OverrideKind = "run_host_allow" // a host the person adds to the run's allowed list, on top of the base policy OverrideRunHostWildcard OverrideKind = "run_host_wildcard" // the same, spelled with a wildcard OverrideRunHostDeny OverrideKind = "run_host_deny" // a host the person adds to the run's denied list, on top of the base policy OverrideFirstUseApproval OverrideKind = "first_use_approval" // how an unlisted destination is handled, chosen on top of the base policy OverrideAllowAllEgress OverrideKind = "allow_all_egress" // request all other egress, on top of the base policy )
type OverrideNarrowing ¶ added in v0.9.0
type OverrideNarrowing struct {
Kind OverrideKind `json:"kind"`
Op OverrideOp `json:"op"`
Direction OverrideDirection `json:"direction"`
Rule OverrideRule `json:"rule"`
}
OverrideNarrowing is one row of the narrowing table.
func OverrideNarrowingTable ¶ added in v0.9.0
func OverrideNarrowingTable() []OverrideNarrowing
OverrideNarrowingTable returns the table, in its fixed order.
func OverrideRuleFor ¶ added in v0.9.0
func OverrideRuleFor(kind OverrideKind, op OverrideOp) (OverrideNarrowing, bool)
OverrideRuleFor looks one operation up; false for a pair the table has no row for.
type OverrideOp ¶ added in v0.9.0
type OverrideOp string
OverrideOp is what the person does to the entry.
const ( OverrideAdd OverrideOp = "add" OverrideRemove OverrideOp = "remove" // OverrideNarrow reduces a value the source resolved: a smaller capability // set, a subset of repositories, write to read. OverrideNarrow OverrideOp = "narrow" // OverrideSet chooses a run-wide scalar the source also holds. OverrideSet OverrideOp = "set" )
type OverrideRule ¶ added in v0.9.0
type OverrideRule string
OverrideRule is what a person may do with the operation.
const ( // OverrideAllowed: always, because it only takes reach away. OverrideAllowed OverrideRule = "allowed" // OverrideClamped: allowed to ask, and the ceiling and the member grant // pipeline may still drop it; the preview shows what they dropped. OverrideClamped OverrideRule = "ceiling_clamped" // OverrideRefused: never, from a run request. OverrideRefused OverrideRule = "refused" // OverrideWithinPolicy: allowed when no looser than the policy for that // entry, refused otherwise. The check needs the resolved policy, so the // server answers it after the policy fold, not from this row alone. OverrideWithinPolicy OverrideRule = "within_policy" )
type PauseReason ¶ added in v0.8.0
type PauseReason string
PauseReason says why a run's agent is frozen (AgentRun.PausedReason).
const ( PauseWaiting PauseReason = "waiting" // parked on an open request nobody has answered PauseIdle PauseReason = "idle" // unused past pause_idle_after_sec, CPU quiet )
type Person ¶ added in v0.8.0
type Person struct {
Principal string `json:"principal"`
Email string `json:"email,omitempty"`
Issuer string `json:"issuer,omitempty"`
TenantID string `json:"tenant_id,omitempty"`
ObjectID string `json:"object_id,omitempty"`
CreatedBy string `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
FirstSignedInAt *time.Time `json:"first_signed_in_at,omitempty"`
}
Person is an identity an admin created or confirmed before its first sign-in, keyed by the identity provider's subject — or, on Entra ID, by Issuer, TenantID and ObjectID, all three set or none (#1195).
type PersonList ¶ added in v0.8.6
type PersonList struct {
People []PersonSummary `json:"people"`
NextCursor string `json:"next_cursor,omitempty"`
}
PersonList is one page of GET /people. NextCursor is empty on the last page.
type PersonSummary ¶ added in v0.8.6
type PersonSummary struct {
Principal string `json:"principal"`
Email string `json:"email,omitempty"`
IssuerKind string `json:"issuer_kind"`
PreCreated bool `json:"pre_created"`
FirstSignedInAt *time.Time `json:"first_signed_in_at,omitempty"`
LastSignedInAt *time.Time `json:"last_signed_in_at,omitempty"`
DeactivatedAt *time.Time `json:"deactivated_at,omitempty"`
Role string `json:"role,omitempty"`
ActiveSessions int `json:"active_sessions"`
APITokens int `json:"api_tokens"`
SSHKeys int `json:"ssh_keys"`
Credentials int `json:"credentials"`
ActiveRuns int `json:"active_runs"`
}
PersonSummary is one row of GET /people: a person this deployment knows, with the counts behind the leaver actions. ActiveSessions is 0 or 1: sessions are stateless cookies, so it says whether the last sign-in could still hold a live one, not how many cookies are out. Role is the role the person's email and last verified groups derive: "denied" when sign-in would refuse them, "unknown" when that sign-in's groups were truncated, and empty when sign-in is not SSO.
type ProviderAuth ¶ added in v0.8.0
type ProviderAuth struct {
Header string `json:"header,omitempty"`
Format string `json:"format,omitempty"`
}
ProviderAuth is how a custom endpoint expects each person's token: header and value format ("Bearer %s").
type ProviderHarness ¶ added in v0.8.0
type ProviderHarness struct {
Harness string `json:"harness"` // harness-catalog id ("claude-code", "codex-cli")
// Model: model id this harness uses on this provider; admin-set, no member override. Required
// (an inference profile) on a Bedrock kind.
Model string `json:"model,omitempty"`
// FastModel is the deployment the Messages harness uses for its small-model alias; azure_foundry only,
// on that harness only. Unset means Model.
FastModel string `json:"fast_model,omitempty"`
Path string `json:"path,omitempty"` // where a custom endpoint serves this harness's API dialect, under BaseURL; may not change the host
// AuthHeader and AuthFormat override the provider's Auth for this harness alone (custom_endpoint only).
AuthHeader string `json:"auth_header,omitempty"`
AuthFormat string `json:"auth_format,omitempty"`
}
ProviderHarness is one harness a provider is enabled for, and the settings for that pairing.
type PushContentScope ¶ added in v0.8.0
type PushContentScope struct {
// Repo is the repository as the run's grant names it: "github.com/<owner>/<repo>"
// on the GitHub App lane, "<host>/<path>" on the git_pat lane (Azure DevOps:
// "dev.azure.com/<org>/<project>/_git/<repo>").
Repo string `json:"repo"`
// Branch is the ref the push updates, "refs/heads/…"; several names join
// sorted by ", ".
Branch string `json:"branch"`
// ActsAs is the credential the forwarded push authenticates with, as
// "<grant kind>:<grant id>" — "github_token:<uuid>" or "git_pat:<uuid>".
ActsAs string `json:"acts_as"`
// Paths are the first PushContentMaxPaths review-matched paths, sorted,
// each git-quoted (core.quotePath on, the sidecar's quotePath) so every
// path is valid UTF-8 on the wire. A path ending in "/" is a directory the
// push does not carry.
Paths []string `json:"paths"`
// PathsTotal is how many paths matched a review pattern in all.
PathsTotal int `json:"paths_total"`
// Commits are the object ids the push sets its refs to, sorted.
Commits []string `json:"commits"`
// Updates pairs each ref the push updates with the object id it sets it
// to — all zeros for a delete — sorted by ref. Branch and Commits name the
// same refs and ids for the card; Updates is what binds one to the other,
// so the same commits sent to the same refs in a different assignment are
// a different scope. A row raised before it existed has none and matches
// no new raise.
Updates []PushRefUpdate `json:"updates"`
// PathsDigest is the lower-case hex SHA-256 over every review-matched path,
// sorted, each followed by a NUL byte (a git path cannot contain one).
PathsDigest string `json:"paths_digest"`
// ActsAsKind and ActsAsLabel are SERVER-SET: the control plane resolves
// ActsAs against the run's own grants and stamps them before the row is
// stored, refusing a raise that carries either — a sidecar's word on who a
// push acts as is not taken. ActsAsKind is one of the PushActsAs*
// constants; ActsAsLabel is the principal the console names.
ActsAsKind string `json:"acts_as_kind,omitempty"`
ActsAsLabel string `json:"acts_as_label,omitempty"`
}
PushContentScope is the requested_scope of an ApprovalPushContent row — the bytes the console renders on the push card, stored verbatim.
The dedup key is the whole scope: approval.RequestApproval and the partial unique index from migration 0022 both compare it, so two raises of the same push collapse into one PENDING row. What makes it the right key is Commits plus PathsDigest: commits are content addresses, so a repack of the same commits (git's retry after an approval) yields the same scope where a pack digest would not, and PathsDigest covers every review-matched path, not just the ten Paths names, so two pushes whose first ten matches agree are still two questions.
func (PushContentScope) Validate ¶ added in v0.8.0
func (s PushContentScope) Validate() error
Validate checks the shape the proxy writes, so a row the console renders can be trusted to be one: every bound here is one the sidecar already honours.
type PushPathList ¶ added in v0.8.0
PushPathList is a held push's complete review-matched path list, sorted and bounded: every path when Truncated is false, else the first ones that fit the bounds. Sent by the sidecar beside the push_content requested_scope; the control plane verifies it against that scope (VerifyAgainst) and keeps it immutable per approval.
func NewPushPathList ¶ added in v0.8.0
func NewPushPathList(paths []string) PushPathList
NewPushPathList bounds paths, which are sorted and distinct.
func (PushPathList) VerifyAgainst ¶ added in v0.8.0
func (l PushPathList) VerifyAgainst(s PushContentScope) error
VerifyAgainst checks l is the list s was built from: sorted, distinct and within bounds, opening with every one of s.Paths, and — unless Truncated — exactly PathsTotal paths whose digest is s.PathsDigest. A truncated list must also have been cut by a bound (PushPathListMaxPaths paths, or within pushPathListSlack of PushPathListMaxBytes); which path follows the cut is still the sidecar's word, since nothing the scope carries names it.
type PushRefUpdate ¶ added in v0.8.0
PushRefUpdate is one ref a held push sets and the object id it sets it to.
type PushRuleSet ¶ added in v0.9.0
type PushRuleSet struct {
Provider string `json:"provider"`
Host string `json:"host,omitempty"`
Org string `json:"org"`
// BasePath is the path prefix of an Azure DevOps Server provider row (for
// example "/tfs"), recorded from the row at dispatch. The git route derives
// a remote's collection with it: without it a remote under a prefix is
// ambiguous and its push is refused. Empty for every other entry.
BasePath string `json:"base_path,omitempty"`
Rules PushRulesSpec `json:"rules"`
}
PushRuleSet is the push content rules of one SCM entry (provider host and organisation), wherever the entry came from: a repository or workspace, a tool or a component. It is the dispatch-plan carrier the proxy's git route reads: the set of the entry that owns the remote applies, and a remote whose entry has no set has no content rules. Host empty is the provider's public host (github.com, dev.azure.com); a GitHub Enterprise Server or Azure DevOps Server names its own. The run-wide RunPolicySpec.PushRules remains only as the older-client mapping, which the dispatch plan fans out to one set per entry the run brings.
The sets are the product AFTER the ceiling: the dispatch plan lays the policy's and the governance ceiling's rules into each entry's set first, and a request's rules (RunOverrides.PushRules) may only ADD a deny or a require-review path to them, never remove or relax one. A person's rules therefore cannot displace an administrator's.
func (PushRuleSet) Key ¶ added in v0.9.0
func (s PushRuleSet) Key() string
Key is the entry this set belongs to, "" when the set does not name one (ValidatePushRuleSets refuses such a set).
type PushRulesOverlay ¶ added in v0.8.6
type PushRulesOverlay struct {
DenyPaths *[]string `json:"deny_paths,omitempty"`
MaxInspectPackMiB *int `json:"max_inspect_pack_mib,omitempty"`
RequireReviewPaths *[]string `json:"require_review_paths,omitempty"`
HoldSeconds *int `json:"hold_seconds,omitempty"`
DenyNewExecutables *bool `json:"deny_new_executables,omitempty"`
MaxFileSizeMiB *int `json:"max_file_size_mib,omitempty"`
}
PushRulesOverlay mirrors PushRulesSpec with per-field presence.
type PushRulesSpec ¶ added in v0.8.0
type PushRulesSpec struct {
// DenyPaths are path patterns (e.g. ".github/workflows/**") the broker
// refuses in a push: "**" crosses path segments, "*" and "?" do not, and
// the pattern is anchored at the repository root. A trailing "/" means
// everything beneath the directory (DenyPathSegments).
DenyPaths []string `json:"deny_paths,omitempty"`
// MaxInspectPackMiB caps how much of an incoming push the broker buffers
// before refusing it as too large. 0/absent takes the broker's own default,
// below the authored maximum so raising this is a real remedy; bounded
// 0..64 by validatePolicySpec.
MaxInspectPackMiB int `json:"max_inspect_pack_mib,omitempty"`
// RequireReviewPaths are patterns in DenyPaths' language whose match HOLDS
// a push for an admin's decision instead of refusing it. A deny match wins
// over a review match; an unattended run refuses rather than holding.
RequireReviewPaths []string `json:"require_review_paths,omitempty"`
// HoldSeconds is how long a held push waits for that decision before it
// is refused. 0/absent means 120; bounded 0..600 by validatePolicySpec.
HoldSeconds int `json:"hold_seconds,omitempty"`
// DenyNewExecutables is reserved: the broker cannot yet tell a newly added executable from an
// edit to an existing one, so validation refuses it when true rather than store a rule nothing
// enforces, and IsSet does not count it, so a stored true never makes the broker inspect for
// a rule it cannot apply.
DenyNewExecutables bool `json:"deny_new_executables,omitempty"`
// MaxFileSizeMiB refuses a push that introduces a file larger than this many MiB, or one whose
// size the pack does not carry and the forge cannot show unchanged. 0/absent is off; bounded
// 0..1024 by validatePolicySpec.
MaxFileSizeMiB int `json:"max_file_size_mib,omitempty"`
}
PushRulesSpec declares content rules for a run's brokered git pushes — the counterpart to GitPushAnyBranch's branch confinement: WHAT a push may touch, not WHERE it may land. A closed struct, deliberately not a free-form rules map: an open map evades the strict-field JSON decoder and the doc-census test, so a typo'd key would silently do nothing instead of failing at write time. This type stores and validates; how DenyPathSegments entries MATCH is the git broker's (internal/egress/proxy/push_rules.go), reading them on both brokered lanes.
func PushRulesForRemote ¶ added in v0.9.0
func PushRulesForRemote(sets []PushRuleSet, provider, remote string) (spec PushRulesSpec, found bool, err error)
PushRulesForRemote is the git route's lookup: the set of the entry that owns remote. found false means the entry has no content rules; it never falls back to another entry's. A remote whose entry cannot be derived is an error, and the caller refuses the push rather than treating it as having no rules.
func PushRulesForRemoteAt ¶ added in v0.9.0
func PushRulesForRemoteAt(sets []PushRuleSet, provider, remote, basePath string) (spec PushRulesSpec, found bool, err error)
PushRulesForRemoteAt is PushRulesForRemote for a provider row whose Azure DevOps Server lives under basePath (for example "/tfs").
func PushRulesForServerRemote ¶ added in v0.9.0
func PushRulesForServerRemote(sets []PushRuleSet, provider, remote, basePath string) (spec PushRulesSpec, found bool, err error)
PushRulesForServerRemote is PushRulesForRemoteAt over PushRuleEntryOfServerRemote.
func (*PushRulesSpec) IsSet ¶ added in v0.8.0
func (s *PushRulesSpec) IsSet() bool
IsSet reports whether this spec carries an actual rule — NOT a bare != nil. An all-zero-but-non-nil *PushRulesSpec (a literal `push_rules: {}`) says nothing about what a push may touch and must read exactly like an absent one wherever consulted (composer's clamp/risk grade, the broker's advertisement/enforcement) — one method so those readers can't drift.
type RemoteRunner ¶ added in v0.9.0
type RemoteRunner struct {
ID string `json:"id"`
Kind RunnerKind `json:"kind"`
Name string `json:"name"`
// PeoplePools is who may add it to their own user pools; PeopleSubjects lists them when it is
// listed. A listed policy whose people all left lists nobody and admits nobody.
PeoplePools RemoteRunnerPeoplePools `json:"people_pools"`
PeopleSubjects []RunnerPoolSubject `json:"people_subjects,omitempty"`
// Limits is what an administrator declared (proposed once at boot from what the server measured);
// nil while any of them is unset, and then the runner takes no run.
Limits *RunnerLimits `json:"limits"`
// Revision starts at 1 and rises on every change to the name or the people policy.
Revision int64 `json:"revision"`
UpdatedBy string `json:"updated_by,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
RemoteRunner is a remote-hosted runner: a Docker host or Kubernetes cluster this server is set up with, keyed by the name of the substrate it is configured as. A row whose substrate this server does not run stays, and is never a candidate.
type RemoteRunnerPeoplePools ¶ added in v0.9.0
type RemoteRunnerPeoplePools string
RemoteRunnerPeoplePools is a remote-hosted runner's "Available to people's pools" policy: who may add it to their own user pools. Off by default; checked when a person adds it and again at launch.
const ( PeoplePoolsOff RemoteRunnerPeoplePools = "off" PeoplePoolsEveryone RemoteRunnerPeoplePools = "everyone" PeoplePoolsListed RemoteRunnerPeoplePools = "listed" )
func (RemoteRunnerPeoplePools) Valid ¶ added in v0.9.0
func (p RemoteRunnerPeoplePools) Valid() bool
Valid reports whether p is one of the three.
type RemoteRunnerPoolRef ¶ added in v0.9.0
type RemoteRunnerPoolRef struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Scope RunnerPoolScope `json:"scope"`
// Owner is a user pool's owner, set on the operators' reads only.
Owner string `json:"owner,omitempty"`
}
RemoteRunnerPoolRef names one live pool a remote-hosted runner is in.
type ResolvedDrive ¶ added in v0.7.0
type ResolvedDrive struct {
Drive UserDrive
Grant UserDriveGrant
Tier CapabilitySubjectType
HomeName string // per-user segment: the subdirectory of a share, or the suffix of a managed object's name
// SubjectHash fingerprints the principal HomeName was derived FROM
// (DriveSubjectHash), carried so the runner can stamp it and refuse one
// stamped for somebody else.
SubjectHash string `json:"subject_hash,omitempty"`
ObjectName string // what the runner asks the substrate for — a volume name, a PVC name, or an absolute host path
SizeMiB int
Writable bool
Enforcement StorageEnforcement
// Paused is set when the grant that WON is disabled: nothing is derived
// and nothing may be mounted, but the row still wins its tier (DESIGN
// §2.2) rather than falling through.
Paused bool
}
ResolvedDrive is THE answer for one principal: the winning drive, the grant that won, the tier it won at, and everything derived from the pair. Built once by the API resolver so no consumer re-derives a home name or override. Writable is already folded but NOT yet narrowed by the run request.
NO json tags: this type crosses no wire. The preview and /me each compose their own response struct from it.
type ResolvedInjection ¶ added in v0.4.4
type ResolvedInjection struct {
Host string `json:"host"`
Header string `json:"header"`
Value string `json:"value"`
JTI string `json:"jti"`
ExpiresAt int64 `json:"expires_at,omitempty"`
// Organisation is the ADO org a per-person credential was dispatched for — informational only;
// the proxy's REST gate pins the org from its own dispatch-time config, never from a resolve.
Organisation string `json:"organisation,omitempty"`
// Capabilities is the same lane's granted capability set, read once to confirm an asked-for
// capability came back granted. Empty on other lanes.
Capabilities []string `json:"capabilities,omitempty"`
}
ResolvedInjection is the wire contract of GET /api/v1/internal/injection/{grantID}: header name plus the server-formatted secret value. ExpiresAt (unix ms, 0 = never) marks a credential the proxy must re-resolve before it lapses; only a single-use approval-gated grant leaves it 0.
type ResolvedRunnerPool ¶ added in v0.9.0
type ResolvedRunnerPool struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Scope RunnerPoolScope `json:"scope"`
Revision int64 `json:"revision"`
Selection RunnerPoolSelection `json:"selection"`
}
ResolvedRunnerPool is the pool a request resolves to: the pool, the revision it was read at and how it was chosen. It is a label for the choice and never admission: a run is admitted against the persisted pool and the exact target.
type ResourceLimits ¶
type ResourceLimits struct {
CPUMillis int `json:"cpu_millis,omitempty"` // milli-CPU; 2000 = 2 vCPU
MemoryMiB int `json:"memory_mib,omitempty"` // hard memory cap in MiB
PidsLimit int `json:"pids_limit,omitempty"` // max processes/threads (fork-bomb guard)
DiskMiB int `json:"disk_mib,omitempty"` // writable storage cap in MiB (best-effort)
}
ResourceLimits caps a sandbox's resource consumption. A ZERO field means "use the platform default": the dispatch path fills conservative defaults (e.g. 2000m CPU, 4096 MiB, 512 PIDs) so even a policy that sets no limits still runs capped. DiskMiB is best-effort and depends on the storage driver supporting a per-container quota (fail-closed/warn when a cap is demanded but unsupported).
type ResourcesOverlay ¶ added in v0.8.6
type ResourcesOverlay struct {
CPUMillis *int `json:"cpu_millis,omitempty"`
MemoryMiB *int `json:"memory_mib,omitempty"`
PidsLimit *int `json:"pids_limit,omitempty"`
DiskMiB *int `json:"disk_mib,omitempty"`
}
ResourcesOverlay mirrors ResourceLimits with per-field presence.
type RoleMapping ¶ added in v0.7.0
type RoleMapping struct {
ID uuid.UUID `json:"id"`
Value string `json:"value"`
Role string `json:"role"`
UserType string `json:"user_type,omitempty"`
MigratedFromMember bool `json:"migrated_from_member,omitempty"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by,omitempty"`
}
RoleMapping is one console-managed (Getting Started -> People) row of migration 0051's role_mappings table: "value maps to role". This is the STORE'S wire type, carrying id/timestamps/provenance — distinct on purpose from internal/auth/oidc's own RoleMapping (just Value/Role), which stays dependency-free of this package (see that type's doc comment) the same way oidc.SessionRevocations keeps oidc dependency-free of store; the API layer converts between the two, mirroring however SessionRevocations bridges store -> oidc today.
Value is expected already canonical (trimmed, lowercased, ASCII) by the API write boundary that owns writes to this table — see the migration comment.
UserType is the row's user type when Role is the user tier (migration 0070_user_tier_rename's column, a foreign key to user_types); "" on a tier row, and on a user row written before types existed, which reads as the built-in "standard".
MigratedFromMember (migration 0098) marks a row that rewrite rewrote from role='member' rather than one an admin actually saved on the built-in type — the store's UpsertRoleMapping clears it on any write that flips the row in place, since choosing a real type is what the marker exists to prompt.
type RunAttention ¶ added in v0.8.0
type RunAttention struct {
Kind AttentionKind `json:"kind"`
By AttentionBy `json:"by"`
Pending int `json:"pending"`
}
RunAttention is GET /runs and GET /me/attention's projection onto one live run: what it's waiting on, who can clear it (in the caller's view), and how many PENDING approvals it carries. Nil when attention() has nothing to say (terminal, lease-ended, nothing PENDING held).
type RunComponent ¶ added in v0.8.9
type RunComponent struct {
RunID uuid.UUID `json:"run_id"`
// Ordinal is the component's position in the request.
Ordinal int `json:"ordinal"`
// SelfDefined is true for an inline component or one the launcher owns;
// such a run needed the custom-component feature. False is an org
// component, whose ComponentID names its own grant.
SelfDefined bool `json:"self_defined"`
// Erased is true once the person who defined it was erased: every field
// below is then empty.
Erased bool `json:"erased,omitempty"`
// ComponentID is nil for an inline component.
ComponentID *uuid.UUID `json:"component_id,omitempty"`
// Owner is "" for an org component, else the person who defined it (the
// launcher).
Owner string `json:"owner,omitempty"`
Name string `json:"name"`
// Version is the stored component's version at launch; 0 for inline.
Version int `json:"version"`
Definition ComponentDefinition `json:"definition"`
// MCPHarnesses are the harnesses an MCP component's server was turned on
// for at launch. Not personal data: it survives an erasure, so a revive
// still knows which harness reached which server.
MCPHarnesses []string `json:"mcp_harnesses,omitempty"`
}
RunComponent is the snapshot of one component as a run was launched with it. It is a copy, not a reference: later edits to, or deletion of, the stored component do not change it.
It is also the run's authorization record. Erasing the person who defined it clears every descriptive field and sets Erased; RunID, Ordinal and SelfDefined survive, so a revive still knows which doors the run needed.
type RunEndReason ¶ added in v0.9.0
type RunEndReason string
RunEndReason says why the platform ended a run. It is the audit row's `reason` and the run detail's sentence, and it rides the run's status_detail as "lifetime: <reason>" when the run is KILLED at its end or its maximum.
const ( // RunEndMaxLifetimeReached: the run reached its effective maximum lifetime and was KILLED. RunEndMaxLifetimeReached RunEndReason = "max_lifetime_reached" // The five ways the pool queue ends a run that waited (docs/design/0.9/pool-queue.md section 9). // A run that waited too long, or whose profile, policy or portal changed under it, is FAILED; a // run whose pool was removed is KILLED. identity_deactivated is the existing token, FAILED here. RunEndQueueTimeout RunEndReason = "queue_timeout" RunEndQueueProfileChanged RunEndReason = "queue_profile_changed" RunEndQueuePolicyChanged RunEndReason = "queue_policy_changed" RunEndQueuePortalRevoked RunEndReason = "queue_portal_revoked" RunEndRunnerPoolRemoved RunEndReason = "runner_pool_removed" RunEndIdentityDeactivated RunEndReason = "identity_deactivated" )
const RunEndEndReached RunEndReason = "end_reached"
RunEndEndReached: a Background task reached its own end time, which came before its maximum lifetime, and was KILLED.
func (RunEndReason) Sentence ¶ added in v0.9.0
func (r RunEndReason) Sentence() string
Sentence is what the run's detail page and the audit view say.
func (RunEndReason) Valid ¶ added in v0.9.0
func (r RunEndReason) Valid() bool
Valid reports whether r is a defined end reason.
type RunEvidenceSource ¶ added in v0.9.0
type RunEvidenceSource string
RunEvidenceSource records who vouches for the evidence a run produces: its confinement, decisions, recordings, exit codes and capabilities. It is not the run's `confinement_source` (`requested` or `defaulted`, written by run.create): that says who chose the class, this says who can attest it.
const ( RunEvidenceSubstrate RunEvidenceSource = "substrate" // the organisation's own substrate RunEvidenceRunnerAsserted RunEvidenceSource = "runner_asserted" // the runner said so; nobody can verify it )
type RunExperience ¶ added in v0.9.0
type RunExperience string
RunExperience is the canonical run mode a person chose when the run was launched: a Background task or an Interactive environment (0.9 New Run).
const ( // ExperienceBackground is a run with no interactive terminal, SSH, // interactive exec or attach, web application gateway or graphical desktop. // Logs, audit, status and the authorised lifecycle controls stay. ExperienceBackground RunExperience = "background" // ExperienceInteractive is general sandboxed work through whatever // interactive surfaces the placement actually supports. ExperienceInteractive RunExperience = "interactive" )
func (RunExperience) Valid ¶ added in v0.9.0
func (e RunExperience) Valid() bool
Valid reports whether e is one of the two modes. The empty value is not a mode.
type RunHarness ¶ added in v0.9.0
type RunHarness struct {
Harness string `json:"harness"`
ModelProviderID string `json:"model_provider_id,omitempty"`
Starts bool `json:"starts,omitempty"`
}
RunHarness is one harness a run was launched with and the model provider it was granted ("" for none). The rows are the run's authorisation record: a renew, revive or queued start re-checks the doors each one needed. Starts marks the one harness the sandbox starts; at most one row per run has it.
type RunLayout ¶ added in v0.5.0
type RunLayout struct {
Preset string `json:"preset"`
Layout []RunLayoutWidget `json:"layout"`
UpdatedAt time.Time `json:"updated_at"`
}
RunLayout is a human's saved widget arrangement for the run-detail cockpit (GET/PUT /api/v1/me/run-layout), one row per (principal, preset) — see migration 0037_ui_layouts.sql and internal/api/ui_layout.go's doc comment for why this is a server row and not ui/src/app/lib/storage.ts's localStorage. Principal deliberately does not appear here: it is the row's SCOPE (how internal/store looks a layout up), never a value the API echoes back or a caller supplies — internal/api/ui_layout.go scopes every read and write by principalFromRequest(r) alone.
type RunLayoutWidget ¶ added in v0.5.0
type RunLayoutWidget struct {
Widget string `json:"widget"`
X int `json:"x"`
Y int `json:"y"`
W int `json:"w"`
H int `json:"h"`
}
RunLayoutWidget places one evidence widget on the run cockpit's grid. Widget is validated server-side against a closed id set (internal/api/ui_layout.go's runLayoutWidgetIDs) before a layout is ever stored — an id no build renders would otherwise be a widget that silently vanishes on restore, with nothing to explain why.
type RunLimits ¶ added in v0.8.0
type RunLimits struct {
// MaxEndAheadSec: furthest ahead of NOW a run's end may be set (0 = no limit).
// Extending within it never needs UserChangesLimits — extending is the lease.
MaxEndAheadSec int `json:"max_end_ahead_sec,omitempty"`
// DefaultEndSec is a new run's end, from create; 0 means MaxEndAheadSec, and
// a run has no end when both are 0.
DefaultEndSec int `json:"default_end_sec,omitempty"`
// AllowNoEnd offers "No end" to a user who may change limits.
AllowNoEnd bool `json:"allow_no_end,omitempty"`
// MaxWaitSec/DefaultWaitSec: longest and default wait for a decision; 0 =
// the deployment's approval expiry, which also caps both.
MaxWaitSec int `json:"max_wait_sec,omitempty"`
DefaultWaitSec int `json:"default_wait_sec,omitempty"`
// UserChangesLimits is the one gate: the user may shorten the end, set No
// end, or change the wait.
UserChangesLimits bool `json:"user_changes_limits,omitempty"`
// PauseIdleAfterSec pauses an unused run after this long (0 pauses only
// runs waiting for a decision).
PauseIdleAfterSec int `json:"pause_idle_after_sec,omitempty"`
}
RunLimits bound how long a run lives and how long it waits for a decision (long-holds design rev 4, §2.2). Zero values keep today's behaviour: no end, the deployment's approval expiry as the wait, no idle pause.
They bind every run under a profile, including a security admin's; only a super admin's run skips them (effectiveCeiling resolves no profile for an operator).
type RunPolicy ¶
type RunPolicy struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Spec RunPolicySpec `json:"spec"`
}
RunPolicy is the declarative policy attached to runs: egress allowlist, approval rules, and the maximum credential scopes a run may be granted. Workspace-local configuration may only NARROW a policy, never widen it.
type RunPolicySpec ¶
type RunPolicySpec struct {
// AllowedDomains is the L2 egress allowlist (exact hosts or "*." wildcards).
AllowedDomains []string `json:"allowed_domains"`
// DeniedDomains always wins over AllowedDomains.
DeniedDomains []string `json:"denied_domains,omitempty"`
// AllowAllEgress switches L2 egress from default-deny to "allow all (deny-list only)" mode: the
// proxy allows ANY non-denied PUBLIC host, AllowedDomains may be empty. SECURITY: denied_domains
// STILL wins, the SSRF/private-IP guard is unaffected, and credential injection STILL requires
// an EXACT allowlist entry, so allow-all never widens where a secret may be injected.
AllowAllEgress bool `json:"allow_all_egress,omitempty"`
// FirstUseApproval controls how an unknown domain is handled: always_deny,
// deny_with_review, or wait_for_review. Accepts the legacy boolean on the
// wire. Inert under allow-all.
FirstUseApproval FirstUseMode `json:"first_use_approval"`
// FirstUseHoldSeconds bounds how long a wait_for_review connection is HELD
// open awaiting a decision before the proxy refuses it. 0/absent keeps the
// built-in 30s default; a positive value overrides it. Only wait_for_review
// holds. See internal/egress/proxy configureHold.
FirstUseHoldSeconds int `json:"first_use_hold_seconds,omitempty"`
// MaxHolds caps concurrent wait_for_review holds. 0/absent keeps the
// built-in 16 default. The (N+1)th concurrent held connection fails fast
// rather than consuming an unbounded goroutine.
MaxHolds int `json:"max_holds,omitempty"`
// AllowedMethods optionally restricts HTTP methods (empty = all).
AllowedMethods []string `json:"allowed_methods,omitempty"`
// MinConfinementClass refuses to launch below this class.
MinConfinementClass ConfinementClass `json:"min_confinement_class"`
// EligibleGrants is the ceiling of credential scopes a run may request.
EligibleGrants []GrantSpec `json:"eligible_grants,omitempty"`
// AutoStopAfter stops idle sandboxes after this many seconds. 0/absent
// means never reaped, same as an explicit negative (the reaper skips <=
// 0); negative exists only to state never-reap intent explicitly.
AutoStopAfterSec int `json:"auto_stop_after_sec,omitempty"`
// WorkspaceMounts are host bind mounts injected into the sandbox. TRUST BOUNDARY: authored only
// on a stored policy or inline create-run request by an admin/SSO-gated operator, never chosen
// by the in-sandbox agent. validatePolicySpec runs runner.ValidateMount (the same deny-list the
// docker driver enforces) at write time and again, defense-in-depth, at sandbox-create.
WorkspaceMounts []WorkspaceMount `json:"workspace_mounts,omitempty"`
// WorkspaceRepos are additional git repos attached to a run, paralleling WorkspaceMounts for
// git-cloned (rather than bind-mounted) sources. Same admin/inline authoring surface and trust
// boundary as WorkspaceMounts: never agent-chosen. validatePolicySpec enforces a unique-target
// invariant across ALL WorkspaceMounts + WorkspaceRepos dests, so a clone can never land on a
// bind target or shadow another repo's checkout.
WorkspaceRepos []WorkspaceRepo `json:"workspace_repos,omitempty"`
// LLMInspection optionally enables OUTBOUND content inspection at the proxy for brokered LLM
// routes. Nil/omitted => OFF (the safe default). A guardrail + visibility layer, NOT
// exfiltration prevention (see internal/contentscan + threatmodel §5.1).
LLMInspection *LLMInspectionSpec `json:"llm_inspection,omitempty"`
// UIApps declares the in-sandbox loopback HTTP apps the UI gateway may relay to a browser.
// OPERATOR-AUTHORED, same trust boundary as WorkspaceMounts: an app is a NAME, a loopback PORT
// and a PATH — never a command string. The gateway serves ONLY a declared port; `ssh -L` stays
// the undeclared-port escape hatch.
UIApps []UIApp `json:"ui_apps,omitempty"`
// Resources caps sandbox CPU/memory/PIDs/disk. Nil, or a zero field, means "use the platform
// default" — dispatch fills conservative defaults so EVERY run is capped, the multi-tenant
// safety control that keeps one runaway/prompt-injected agent from OOM-killing the host,
// fork-bombing it, or filling storage and taking down sibling runs.
Resources *ResourceLimits `json:"resources,omitempty"`
// ToolRules narrows an autonomous run's tool use from a BINARY (`tool_approvals`'s `auto`/`hold`)
// to a policy: naming a TOOL and an effect (allow/hold/deny) lets "read freely, ask before shell,
// never fetch the web" become expressible. Operator-authored and evaluated outside the sandbox
// (proxy-side), so a compromised agent can't rewrite its own rules; it only narrows, and applies
// only to a run already in `hold`, so a bad rule at worst asks more or refuses a call. Empty
// preserves today's behaviour.
ToolRules []ToolRule `json:"tool_rules,omitempty"`
// GitPushAnyBranch turns OFF branch-namespace confinement for THIS run's brokered pushes (GitHub
// App, opted-in git_pat and Azure DevOps Entra lanes). By default the broker forwards a push only
// under refs/heads/wardyn/<run-id>/, so an agent can't rewrite main — right for an autonomous run,
// wrong for a human-driven sandbox using an external tool with its own branch names. true =
// forward to any branch the granted token may write, audited per push; the grant's own GitHub
// ruleset still bounds what the token can touch. Operator-authored, never agent-settable.
//
// ponytail: whole-namespace off, not a per-run allowed-prefix list — add a
// prefix list when someone needs an external tool AND confinement at once.
GitPushAnyBranch bool `json:"git_push_any_branch,omitempty"`
// PushRules declares CONTENT rules for this run's brokered git pushes — WHAT a push may touch,
// alongside GitPushAnyBranch's WHERE. Nil (every pre-existing policy) means no content rules,
// byte-identical to that prior wire shape, since every reader keys off IsSet. A policy that sets
// PushRules while this run's only git-capable grant is ssh_key is legal but UNENFORCEABLE (SSH
// has no broker seam) — composer.Grade surfaces that as a medium-risk WARNING, never a refusal.
PushRules *PushRulesSpec `json:"push_rules,omitempty"`
// AzureDevOpsCapabilities chooses THIS run's capabilities on the per-person Azure DevOps lane,
// in place of the provider row's default_profile — which is what lets a saved policy act as a
// saved access profile. It chooses only WITHIN the row's capability_ceiling: dispatch refuses a
// run naming anything outside it, and the resolver re-checks the live ceiling on every request.
// Empty/absent keeps the row's default_profile. Inert for a run not on that lane.
AzureDevOpsCapabilities []adoscope.Capability `json:"azure_devops_capabilities,omitempty"`
// GitHubCapabilities is AzureDevOpsCapabilities' GitHub twin, with the same meaning: THIS
// run's GitHub capabilities in place of the GitHub provider row's default_profile, chosen only
// within the row's capability_ceiling. Empty/absent keeps the row's default_profile. Nothing
// reads it yet — the row block and the lanes that honour it land after this field — so today
// it is stored, narrowed and compared, and inert at dispatch.
GitHubCapabilities []ghscope.Capability `json:"github_capabilities,omitempty"`
}
func (RunPolicySpec) Clone ¶ added in v0.3.1
func (s RunPolicySpec) Clone() RunPolicySpec
Clone returns a deep copy: every slice/pointer field is reallocated, so the copy shares no backing array with the receiver. A plain `spec := other` is a SHALLOW copy — slice headers still point at the original's backing array, making the process-global default policy aliasable: a per-run `append` to AllowedDomains wrote into the shared spare capacity, so two concurrent create-runs raced on the same element (one run's egress domain silently replacing another's), and any in-place mutation leaked into every later run. Callers deriving a per-run/per-request spec from a shared one must Clone first.
type RunQueue ¶ added in v0.9.0
type RunQueue struct {
PoolID uuid.UUID `json:"pool_id"`
QueuedAt time.Time `json:"queued_at"`
DeadlineAt time.Time `json:"deadline_at"`
Position int `json:"position"`
Reason string `json:"reason"`
}
RunQueue is what a QUEUED run says about its wait: the pool, when it joined, when its wait ends, how many runs are ahead of it in that pool (a count, never whose) and the wait reason token (runner_pool_at_capacity, runner_pool_no_runner_free or runner_pool_runners_at_capacity).
type RunState ¶
type RunState string
RunState is the AgentRun lifecycle state machine.
const ( // RunQueued is a launch waiting for a slot in a capped runner pool: non-terminal, before PENDING, // and holding nothing (no identity, no sandbox, no slot). RunQueued RunState = "QUEUED" RunPending RunState = "PENDING" RunStarting RunState = "STARTING" RunRunning RunState = "RUNNING" // RunWaiting is reserved: unused, but kept wired end-to-end for the approval-gated-run feature. RunWaiting RunState = "WAITING_FOR_CONFIRMATION" RunStopped RunState = "STOPPED" RunArchived RunState = "ARCHIVED" RunFailed RunState = "FAILED" RunKilled RunState = "KILLED" // RunCompleted is terminal success (exit 0); a non-zero exit becomes RunFailed instead. RunCompleted RunState = "COMPLETED" )
func (RunState) IsTerminal ¶ added in v0.4.4
IsTerminal reports whether the run has ended. SINGLE source of the terminal set — the UI's copy (runs.ts) is pinned equal by TestTerminalRunStates_UIParity.
type RunTemplate ¶ added in v0.9.0
type RunTemplate struct {
ID uuid.UUID
Owner TemplateOwner
Kind TemplateKind
Name string
Description string
Revision int
Document json.RawMessage
CreatedAt time.Time
CreatedBy string
UpdatedAt time.Time
UpdatedBy string
}
RunTemplate is one stored template revision: the catalogue key, the kind, and the name, description and document that revision saved. Document is the door-normalised template document as JSON; the store never reads into it.
type Runner ¶ added in v0.9.0
type Runner struct {
ID uuid.UUID `json:"id"`
Owner string `json:"owner"`
Name string `json:"name"`
OrgURLSHA256 string `json:"-"`
// MintedBy is who minted the registration token this runner redeemed; empty for a runner
// registered before it was recorded.
MintedBy string `json:"-"`
// PublicKey is the runner's Ed25519 key. The private half never leaves the runner.
PublicKey []byte `json:"-"`
KeyFingerprint string `json:"key_fingerprint"`
State RunnerState `json:"state"`
Version string `json:"version,omitempty"`
CreatedAt time.Time `json:"created_at"`
ClaimedAt *time.Time `json:"claimed_at,omitempty"`
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
Posture RunnerPosture `json:"posture"`
// PostureReportedAt is nil until the runner reports; PostureSource is then runner_asserted.
PostureReportedAt *time.Time `json:"posture_reported_at,omitempty"`
PostureSource string `json:"posture_source,omitempty"`
// RevokedBy is the principal that revoked the runner; RevokeReason is "" or
// RunnerRevokeNotMine. Both are empty until it is revoked.
RevokedBy string `json:"-"`
RevokeReason string `json:"-"`
// MaxRuns is how many sandbox-holding runs placement may put on the runner at once: the runner's
// "Runs at once". Its owner sets it; the stored value is the one placement holds.
MaxRuns int `json:"max_runs"`
// Limits is the runner's declared limit set (its RunsAtOnce is MaxRuns), nil while its owner has not
// set them all (0174): such a runner takes no run.
Limits *RunnerLimits `json:"-"`
// MachineName is the host name the runner last reported (at registration and on each connect):
// runner-reported, never an identity, "" until it reports one.
MachineName string `json:"machine_name"`
}
Runner is one registered wardyn-runnerd. Owner is a person's principal, so the row is personal data.
type RunnerActionKind ¶ added in v0.9.0
type RunnerActionKind string
RunnerActionKind is a pending action's verb.
const ( RunnerActionRetire RunnerActionKind = "retire_run" RunnerActionKill RunnerActionKind = "kill" RunnerActionEnd RunnerActionKind = "end" RunnerActionStopProxy RunnerActionKind = "stop_proxy" RunnerActionRecoverMutation RunnerActionKind = "recover_mutation" RunnerActionQuarantine RunnerActionKind = "quarantine_run" RunnerActionRecoverContainment RunnerActionKind = "recover_containment" )
type RunnerAmounts ¶ added in v0.9.0
type RunnerAmounts struct {
CPUMillis int64 `json:"cpu_millis"`
MemoryMiB int64 `json:"memory_mib"`
DiskMiB int64 `json:"disk_mib"`
}
RunnerAmounts is an amount of CPU (milli-CPU), memory (MiB) and disk (MiB).
func PerRunShare ¶ added in v0.9.0
func PerRunShare(total RunnerAmounts, runsAtOnce int) RunnerAmounts
PerRunShare is each run's share of total when runsAtOnce run side by side: total divided by Runs at once, at least one CPU and one GiB of memory and disk, never past the total.
type RunnerCapacity ¶ added in v0.9.0
type RunnerCapacity struct {
CPUMillisMax int64 `json:"cpu_millis_max"`
MemoryMiBMax int64 `json:"memory_mib_max"`
}
RunnerCapacity is the per-run ceiling a runner advertised: the wire shape of its caps event.
type RunnerClaimRequest ¶ added in v0.9.0
type RunnerClaimRequest struct {
Fingerprint string `json:"fingerprint"`
}
RunnerClaimRequest confirms the fingerprint observed on the owner's host.
type RunnerFilter ¶ added in v0.9.0
type RunnerFilter string
RunnerFilter is the state filter of the administrators' runner list; an empty one is active.
const ( RunnerFilterActive RunnerFilter = "active" RunnerFilterRevoked RunnerFilter = "revoked" RunnerFilterAll RunnerFilter = "all" )
type RunnerKind ¶ added in v0.9.0
type RunnerKind string
RunnerKind is the kind of a runner: a person's registered computer, or a Docker host or Kubernetes cluster this server is set up with.
const ( RunnerKindSelfHosted RunnerKind = "self_hosted" RunnerKindRemoteHosted RunnerKind = "remote_hosted" )
type RunnerLimits ¶ added in v0.9.0
type RunnerLimits struct {
RunsAtOnce int `json:"runs_at_once"`
Total RunnerAmounts `json:"total"`
PerRun RunnerAmounts `json:"per_run"`
}
RunnerLimits is a runner's declared limit set: how many runs it takes at once, what all of them together may reserve (Total) and what one may reserve (PerRun).
func ProposeRunnerLimits ¶ added in v0.9.0
func ProposeRunnerLimits(measured RunnerAmounts, runsAtOnce int) RunnerLimits
ProposeRunnerLimits is what setup offers for a computer that measured as measured (owner ruling RP Q3): half of each as the total, and per run the total divided by Runs at once, at least one CPU and one GiB of memory and disk, never past the total. A measured amount of 0 (not measurable) proposes 0 there, which Validate refuses, so the person is asked for it.
func (RunnerLimits) Validate ¶ added in v0.9.0
func (l RunnerLimits) Validate(maxRuns int) error
Validate refuses a limit set the store's checks would: Runs at once from 1 to maxRuns, each amount within the run request contract's bounds (at least one CPU, disk in whole GiB), and no per-run amount past its total.
type RunnerMutationFailure ¶ added in v0.9.0
type RunnerMutationFailure struct {
ID uuid.UUID `json:"id"`
Generation uuid.UUID `json:"generation"`
Operation string `json:"operation"`
Contained bool `json:"contained"`
}
RunnerMutationFailure is an immutable failure identity. Contained becomes true only after proxy containment and lifecycle persistence both succeed.
type RunnerPendingAction ¶ added in v0.9.0
type RunnerPendingAction struct {
Retirement *RunnerRetirement `json:"retirement,omitempty"`
Quarantine *RunnerQuarantine `json:"quarantine,omitempty"`
Recovery *RunnerMutationFailure `json:"recovery,omitempty"`
ID uuid.UUID `json:"id"`
RunnerID uuid.UUID `json:"runner_id"`
RunID uuid.UUID `json:"run_id"`
Kind RunnerActionKind `json:"kind"`
Ref string `json:"ref"`
QueuedAt time.Time `json:"queued_at"`
AppliedAt *time.Time `json:"applied_at,omitempty"`
Outcome string `json:"outcome,omitempty"`
ObservedAt *time.Time `json:"observed_at,omitempty"`
// Bound, Owner, Generation and LostAt are the org identity persisted when
// the action was queued. A row that is not Bound predates that binding and
// grants nothing. On a Bound row a nil Generation means no execution had
// been issued; LostAt is set for stop_proxy only.
Bound bool `json:"-"`
Owner string `json:"-"`
Generation uuid.UUID `json:"-"`
LostAt *time.Time `json:"-"`
// Asserted marks a subject with no org run row: RunID is then the
// runner's assertion, Owner is empty and Generation is nil.
Asserted bool `json:"-"`
// ObligationID is the chain a deferred action's successors continue.
ObligationID uuid.UUID `json:"-"`
}
RunnerPendingAction is applied before readiness when a runner reconnects.
type RunnerPool ¶ added in v0.9.0
type RunnerPool struct {
ID uuid.UUID `json:"id"`
// Name is a display label a pool's administrator may change. It is never an
// identity: every reference is by ID.
Name string `json:"name"`
Scope RunnerPoolScope `json:"scope"`
// Owner is the person a user pool belongs to (their subject, as runner
// ownership names them): only they change it or put runners in it, and only
// they use it. An organization pool has none: administrators keep it.
Owner string `json:"owner,omitempty"`
State RunnerPoolState `json:"state"`
// Revision starts at 1 and increases by one on every change to the pool, its
// membership or its use policy. A run records the revision it was admitted
// against, and a writer that names a stale revision is refused.
Revision int64 `json:"revision"`
// Limits are the pool's own limits per run type. Absent, the pool adds no
// bound of its own (a pool migrated from before limits existed).
Limits *RunnerPoolLimits `json:"limits,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
RunnerPool is one pool of the catalogue: an organization pool or one person's own user pool.
type RunnerPoolAmount ¶ added in v0.9.0
RunnerPoolAmount is a CPU or memory limit: what an untouched run gets and the most a request may ask for. Both are positive and Default is at most Cap.
type RunnerPoolBackgroundLimits ¶ added in v0.9.0
type RunnerPoolBackgroundLimits struct {
CPUMillis *RunnerPoolAmount `json:"cpu_millis,omitempty"`
MemoryMiB *RunnerPoolAmount `json:"memory_mib,omitempty"`
// DiskMiB bounds the main drive of this run type's runs: what an untouched run
// gets and the most a request may ask for, in MiB, each a whole number of GiB.
// Nil: the pool sets no main-drive bound.
DiskMiB *RunnerPoolAmount `json:"disk_mib,omitempty"`
Lifetime *RunnerPoolDuration `json:"lifetime,omitempty"`
}
RunnerPoolBackgroundLimits are a pool's limits for Background tasks. It has no idle stop: a Background task ends when its command or agent exits, or at its maximum lifetime. Decoding a body that sends `idle` here fails.
Every field is optional: one left out is set by the pool's runners (their per-run limits, and governance and the deployment for time), never "no limit".
type RunnerPoolDefaults ¶ added in v0.9.0
type RunnerPoolDefaults struct {
PoolID *uuid.UUID `json:"pool_id,omitempty"`
Barrier ConfinementClass `json:"barrier,omitempty"`
}
RunnerPoolDefaults is the default pool, for a person or for the organization: the pool a run that names none starts on. A person's own wins over the organization's; it is never a way to widen what a person may use, because a default only ever seeds a choice the catalogue must still permit. The organization's names an organization pool only.
Barrier is the organisation's alone: the class an untouched launch gets when it is at or above the floor and the pool offers it. A person's own document never carries one.
func (RunnerPoolDefaults) Validate ¶ added in v0.9.0
func (d RunnerPoolDefaults) Validate() error
Validate checks the document's shape. Whether the pool exists, is permitted and may be this kind of default is the writer's and the resolver's, against the live catalogue.
type RunnerPoolDuration ¶ added in v0.9.0
type RunnerPoolDuration struct {
DefaultSec int `json:"default_sec"`
MaxSec int `json:"max_sec,omitempty"`
Unlimited bool `json:"unlimited,omitempty"`
}
RunnerPoolDuration is a lifetime or an idle-stop limit, in seconds. It has exactly one of two forms: a finite MaxSec, with DefaultSec from 1 to MaxSec, or Unlimited, with MaxSec 0 and a DefaultSec of 0 (no end, or never stop for idleness, by default) up to RunnerPoolSecondsMax. Unlimited is never encoded by a zero MaxSec alone, so an omitted number cannot read as it. For an idle stop, Unlimited is "never". An Interactive lifetime that is Unlimited still needs a DefaultSec of at least 1; a Background lifetime may default to no end.
type RunnerPoolInteractiveLimits ¶ added in v0.9.0
type RunnerPoolInteractiveLimits struct {
CPUMillis *RunnerPoolAmount `json:"cpu_millis,omitempty"`
MemoryMiB *RunnerPoolAmount `json:"memory_mib,omitempty"`
// DiskMiB is the main drive's bound, as on RunnerPoolBackgroundLimits.
DiskMiB *RunnerPoolAmount `json:"disk_mib,omitempty"`
Lifetime *RunnerPoolDuration `json:"lifetime,omitempty"`
Idle *RunnerPoolDuration `json:"idle,omitempty"`
}
RunnerPoolInteractiveLimits are a pool's limits for Interactive environments, which alone have an idle stop. Every field is optional, as on RunnerPoolBackgroundLimits.
type RunnerPoolLimits ¶ added in v0.9.0
type RunnerPoolLimits struct {
Background *RunnerPoolBackgroundLimits `json:"background,omitempty"`
Interactive *RunnerPoolInteractiveLimits `json:"interactive,omitempty"`
// Barriers are the confinement classes (Fence CC1, Wall CC2, Vault CC3) the
// pool allows. A run on any other is refused. None listed: every barrier its
// runners offer.
Barriers []ConfinementClass `json:"barriers"`
// MaxConcurrentRuns caps the pool's non-terminal runs, across everyone. Unset
// is no pool cap; a person's own cap is the governance profile's.
MaxConcurrentRuns *int `json:"max_concurrent_runs,omitempty"`
}
RunnerPoolLimits is everything a pool limits. A run type with limits is a run type the pool allows, and one without is refused, so "allowed" and "limited" cannot disagree. A pool with no RunnerPoolLimits at all adds no bound: only governance, the deployment and the runner bind its runs.
func (RunnerPoolLimits) AllowsBarrier ¶ added in v0.9.0
func (l RunnerPoolLimits) AllowsBarrier(c ConfinementClass) bool
AllowsBarrier reports whether the pool allows runs on confinement class c.
func (RunnerPoolLimits) AllowsRunType ¶ added in v0.9.0
func (l RunnerPoolLimits) AllowsRunType(t RunnerPoolRunType) bool
AllowsRunType reports whether the pool serves runs of type t.
func (RunnerPoolLimits) RunTypes ¶ added in v0.9.0
func (l RunnerPoolLimits) RunTypes() []RunnerPoolRunType
RunTypes lists the run types the pool allows, Background first.
func (RunnerPoolLimits) Validate ¶ added in v0.9.0
func (l RunnerPoolLimits) Validate() error
Validate refuses limits a pool cannot keep: no run type, no barrier, an unknown or repeated barrier, a default past its cap, a value out of range or a concurrency cap below 1.
type RunnerPoolMember ¶ added in v0.9.0
type RunnerPoolMember struct {
PoolID uuid.UUID `json:"pool_id"`
Kind RunnerKind `json:"kind"`
// RunnerID is a claimed self-hosted runner (user pools only).
RunnerID *uuid.UUID `json:"runner_id,omitempty"`
// RemoteRunnerID is a remote-hosted runner (either scope).
RemoteRunnerID string `json:"remote_runner_id,omitempty"`
AddedAt time.Time `json:"added_at"`
}
RunnerPoolMember is one runner of a pool: exactly one of RunnerID (a self-hosted runner) and RemoteRunnerID (a remote-hosted runner) is set, and Kind says which. A self-hosted membership is added by the runner's own owner into their own user pool and never by anyone on their behalf.
func (RunnerPoolMember) Validate ¶ added in v0.9.0
func (m RunnerPoolMember) Validate(scope RunnerPoolScope) error
Validate refuses a member that names no runner, two runners, or a self-hosted runner in an organization pool.
type RunnerPoolRef ¶ added in v0.9.0
RunnerPoolRef names one pool a runner is a member of.
type RunnerPoolRunType ¶ added in v0.9.0
type RunnerPoolRunType string
RunnerPoolRunType is the kind of run a pool serves. The values are the run mode's wire values (a Background task, an Interactive environment).
const ( RunnerPoolRunBackground RunnerPoolRunType = "background" RunnerPoolRunInteractive RunnerPoolRunType = "interactive" )
func (RunnerPoolRunType) Plural ¶ added in v0.9.0
func (t RunnerPoolRunType) Plural() string
Plural is how a sentence names runs of the type.
func (RunnerPoolRunType) Valid ¶ added in v0.9.0
func (t RunnerPoolRunType) Valid() bool
Valid reports whether t is one of the two run types.
type RunnerPoolScope ¶ added in v0.9.0
type RunnerPoolScope string
RunnerPoolScope says whose a pool is. It is fixed for the life of a pool: an organization pool is the administrators', a user pool its owner's.
const ( // RunnerPoolOrganization pools hold remote-hosted runners only, and have no owner. RunnerPoolOrganization RunnerPoolScope = "organization" // RunnerPoolUser pools belong to one person. RunnerPoolUser RunnerPoolScope = "user" )
func (RunnerPoolScope) Placement ¶ added in v0.9.0
func (s RunnerPoolScope) Placement() Placement
Placement is the run placement a pool of this scope serves today: an organization pool places on its remote-hosted runners, a user pool on its owner's own self-hosted runners (a user pool's remote-hosted members are listed but not yet candidates). Empty for a value outside the set.
func (RunnerPoolScope) Valid ¶ added in v0.9.0
func (s RunnerPoolScope) Valid() bool
Valid reports whether s is one of the two scopes.
type RunnerPoolSelection ¶ added in v0.9.0
type RunnerPoolSelection string
RunnerPoolSelection says how a run's pool was chosen.
const ( // RunnerPoolSelectedExplicit: the request named the pool. RunnerPoolSelectedExplicit RunnerPoolSelection = "explicit" // RunnerPoolSelectedPersonal: the person's own saved default. RunnerPoolSelectedPersonal RunnerPoolSelection = "personal_default" // RunnerPoolSelectedOrg: the organisation's default. RunnerPoolSelectedOrg RunnerPoolSelection = "organisation_default" )
func (RunnerPoolSelection) Valid ¶ added in v0.9.0
func (s RunnerPoolSelection) Valid() bool
Valid reports whether s is one of the three.
type RunnerPoolState ¶ added in v0.9.0
type RunnerPoolState string
RunnerPoolState is a pool's lifecycle. A deleted pool is a tombstone: runs that were bound to it keep their history, and nothing new can choose it.
const ( RunnerPoolActive RunnerPoolState = "active" RunnerPoolDisabled RunnerPoolState = "disabled" RunnerPoolDeleted RunnerPoolState = "deleted" )
func (RunnerPoolState) Valid ¶ added in v0.9.0
func (s RunnerPoolState) Valid() bool
Valid reports whether s is one of the three lifecycle states.
type RunnerPoolSubject ¶ added in v0.9.0
type RunnerPoolSubject struct {
SubjectType CapabilitySubjectType `json:"subject_type"`
Subject string `json:"subject"`
}
RunnerPoolSubject is one person, group or role a pool-use policy admits. It reuses the capability subject vocabulary: a role is a user_type, and an identity provider's roles arrive as groups.
type RunnerPoolUsePolicy ¶ added in v0.9.0
type RunnerPoolUsePolicy struct {
PoolID uuid.UUID `json:"pool_id"`
Subjects []RunnerPoolSubject `json:"subjects"`
// Revision is the pool's revision this policy was written at.
Revision int64 `json:"revision"`
UpdatedAt time.Time `json:"updated_at"`
UpdatedBy string `json:"updated_by,omitempty"`
}
RunnerPoolUsePolicy narrows who may use one organization pool. With no policy, everyone who may launch remote runs may use the pool; with one, only the listed subjects may. It never grants launch rights, so it can only narrow. A policy with no subjects is refused rather than stored: "nobody" and "unrestricted" must not look alike, and a disabled pool already closes one.
func (RunnerPoolUsePolicy) Validate ¶ added in v0.9.0
func (p RunnerPoolUsePolicy) Validate(scope RunnerPoolScope) error
Validate checks a policy before it is stored against the pool it narrows: a use policy narrows an organization pool only. On a user pool it would either be stored and ignored, reading as a restriction that does not exist, or become a sharing ACL over people's own runners, which 0.9 does not have.
type RunnerPosture ¶ added in v0.9.0
type RunnerPosture struct {
MDMManaged bool `json:"mdm_managed"`
DiskEncrypted bool `json:"disk_encrypted"`
OS string `json:"os"`
OSVersion string `json:"os_version"`
// Evidence is where the runner read mdm_managed and disk_encrypted from: a
// root-owned file the device's MDM wrote, else its own probe of the OS.
// Either way the org records the posture as runner_asserted.
Evidence string `json:"evidence"`
}
RunnerPosture is what a runner reported about its device. Self-reported: it separates a lost or compromised laptop from a healthy one and does not constrain a root developer.
type RunnerQuarantine ¶ added in v0.9.0
type RunnerQuarantine struct {
ID uuid.UUID `json:"id"`
Generation uuid.UUID `json:"generation"`
Asserted bool `json:"asserted,omitempty"`
}
RunnerQuarantine identifies an org-authorized isolation disposition, not an evidence acknowledgement or permission to reconstruct missing run authority. Asserted marks a quarantine of a run the org has no row for: Generation is then the runner's own assertion, never compared to org executions, and the runner deletes that run's local evidence after RunnerAssertedEvidenceTTL or at revocation.
type RunnerRegisterRequest ¶ added in v0.9.0
type RunnerRegisterRequest struct {
Token string `json:"token"`
PublicKey []byte `json:"public_key"`
Name string `json:"name"`
// Limits is the runner's limit set, which the server requires: wardyn-runnerd measures the host
// and proposes it (ProposeRunnerLimits), and the person may change any of it.
Limits *RunnerLimits `json:"limits"`
// MachineName is the host's own name (os.Hostname), runner-reported; omitted by an older runner.
MachineName string `json:"machine_name,omitempty"`
}
RunnerRegisterRequest carries only the public half of the runner identity.
type RunnerRegistration ¶ added in v0.9.0
type RunnerRegistration struct {
RunnerID uuid.UUID `json:"runner_id"`
Fingerprint string `json:"fingerprint"`
State RunnerState `json:"state"`
OrgURLSHA256 string `json:"org_url_sha256"`
// Limits is the limit set the server stored; nil on a runner registered before 0174 until its owner sets one.
Limits *RunnerLimits `json:"limits"`
}
RunnerRegistration is safe to return to the anonymous runner; it has no human identity.
type RunnerRegistrationToken ¶ added in v0.9.0
type RunnerRegistrationToken struct {
ID uuid.UUID `json:"id"`
Owner string `json:"owner"`
MintedBy string `json:"minted_by"`
Token string `json:"token,omitempty"`
TokenSHA256 string `json:"-"`
OrgURLSHA256 string `json:"-"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
ConsumedAt *time.Time `json:"consumed_at,omitempty"`
// OrgURL is the organisation URL the runner must register against: the mint answers carry it, a
// token list never does. wardyn-runnerd refuses an --org that does not hash to the server's.
OrgURL string `json:"org_url,omitempty"`
}
RunnerRegistrationToken is a single-use invitation for one owner's runner. The raw token is returned only at mint; stored rows carry its hash.
type RunnerRetirement ¶ added in v0.9.0
type RunnerRetirement struct {
ID uuid.UUID `json:"id"`
Generation uuid.UUID `json:"generation"`
ExecID string `json:"exec_id"`
ExitHash string `json:"exit_hash"`
OutputEnd int64 `json:"output_end"`
Spool RunnerSpoolReceipt `json:"spool"`
}
RunnerRetirement is an org-issued settlement receipt, never authority inferred from a runner's staged exit ACK. Its action separately binds runner/run/ref.
type RunnerSettings ¶ added in v0.9.0
type RunnerSettings struct {
Enabled bool `json:"enabled"`
}
RunnerSettings controls admission to runner routes. Nil and false both deny.
type RunnerSettingsRequest ¶ added in v0.9.0
type RunnerSettingsRequest struct {
Enabled *bool `json:"enabled"`
}
RunnerSettingsRequest turns runners on or off. Enabled is required, so an empty body never switches anything.
type RunnerSpoolReceipt ¶ added in v0.9.0
type RunnerSpoolReceipt struct {
SpoolID uuid.UUID `json:"spool_id"`
Seq int64 `json:"seq"`
Hash string `json:"hash"`
ReportedDrops uint64 `json:"reported_drops"`
}
RunnerSpoolReceipt identifies the durable accepted prefix including overflow.
type RunnerState ¶ added in v0.9.0
type RunnerState string
RunnerState is where a registered runner is in the claim machine.
const ( // RunnerUnclaimed: registered with a token; offered no run and relays nothing. RunnerUnclaimed RunnerState = "unclaimed" // RunnerClaimed: the token's owner proved they hold the key. RunnerClaimed RunnerState = "claimed" // RunnerRevoked is terminal; the key is never reused. RunnerRevoked RunnerState = "revoked" )
type RunnerTally ¶ added in v0.9.0
type RunnerTally struct {
Runs int `json:"runs"`
CPUMillis int64 `json:"cpu_millis"`
MemoryMiB int64 `json:"memory_mib"`
DiskMiB int64 `json:"disk_mib"`
}
RunnerTally is a number of runs and the CPU, memory and disk they reserve.
type RunnerTokenRequest ¶ added in v0.9.0
type RunnerTokenRequest struct {
Owner string `json:"owner"`
}
RunnerTokenRequest names the person an operator is registering a runner for.
type RunnerUpdateRequest ¶ added in v0.9.0
type RunnerUpdateRequest struct {
Limits *RunnerLimits `json:"limits"`
}
RunnerUpdateRequest is an owner's change to their own runner (PUT /me/runners/{id}): the whole limit set, required, so an empty body changes nothing.
type RunnerUsage ¶ added in v0.9.0
type RunnerUsage struct {
Online bool `json:"online"`
InUse RunnerTally `json:"in_use"`
Available RunnerTally `json:"available"`
DiskCapped bool `json:"disk_capped"`
Reported *RunnerAmounts `json:"reported,omitempty"`
}
RunnerUsage is how much of a runner its runs hold. InUse is every run holding a sandbox on it, from every pool and person: what placement counts. Available is the limits' total less InUse, and all zero while the runner is offline or declares no limits. DiskCapped is false where the runner cannot cap a run's disk, so its disk limits are counted, never enforced. Reported is what the runner last said it has, never a limit.
type RunnerView ¶ added in v0.9.0
type RunnerView struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
State RunnerState `json:"state"`
// Owner is set on the administrators' routes only; the caller's own list omits it.
Owner string `json:"owner,omitempty"`
// MintedBy names who minted the registration token this runner redeemed. MintedByEmail is that
// person's email where Wardyn holds one, set on a person's own routes when someone else minted it,
// so the console can say who without printing an opaque sign-in subject.
MintedBy string `json:"minted_by,omitempty"`
MintedByEmail string `json:"minted_by_email,omitempty"`
// Online is the hub's live view of a claimed runner and false for any other.
Online bool `json:"online"`
// KeyFingerprint is abbreviated for an unclaimed runner on its owner's own routes, so it cannot
// be copied from there into the claim; KeyFingerprintAbbreviated says so.
KeyFingerprint string `json:"key_fingerprint"`
KeyFingerprintAbbreviated bool `json:"key_fingerprint_abbreviated"`
Version string `json:"version,omitempty"`
CreatedAt time.Time `json:"created_at"`
// ClaimExpiresAt is when an unclaimed runner lapses; absent for any other state.
ClaimExpiresAt *time.Time `json:"claim_expires_at,omitempty"`
ClaimedAt *time.Time `json:"claimed_at,omitempty"`
LastSeenAt *time.Time `json:"last_seen_at,omitempty"`
RevokedAt *time.Time `json:"revoked_at,omitempty"`
// RevokedBy names who revoked the runner; RevokeReason is "" or RunnerRevokeNotMine. Both are
// absent until it is revoked.
RevokedBy string `json:"revoked_by,omitempty"`
RevokeReason string `json:"revoke_reason,omitempty"`
// RevokedByEmail is that person's email where Wardyn holds one, set on a person's own list only.
RevokedByEmail string `json:"revoked_by_email,omitempty"`
// Posture is absent until the runner has reported; PostureSource is then runner_asserted.
Posture *RunnerPosture `json:"posture,omitempty"`
PostureReportedAt *time.Time `json:"posture_reported_at,omitempty"`
PostureSource string `json:"posture_source,omitempty"`
// Capacity, Classes and AllowedRoots are the claimed runner's last caps event, all runner_asserted.
// They are read from this process's live endpoint, so like Online they are absent when the runner
// has not connected to this replica, never a stored or guessed value.
Capacity *RunnerCapacity `json:"capacity,omitempty"`
Classes []ConfinementClass `json:"classes,omitempty"`
AllowedRoots []string `json:"allowed_roots,omitempty"`
// Kind is always RunnerKindSelfHosted: the one shape both kinds' reads share.
Kind RunnerKind `json:"kind"`
// Limits is what the runner's owner declared, which only they change; nil when not all of them are
// set, and then the runner takes no run. Usage is what its runs hold now, the figures placement
// counts, shown to its owner and to operators only.
Limits *RunnerLimits `json:"limits"`
Usage RunnerUsage `json:"usage"`
// MachineName is the host name the runner last reported at registration or on connect. It is what
// the runner says, never verified and never an identity; absent until it reports one.
MachineName string `json:"machine_name,omitempty"`
// Pools are the owner's live Self-Hosted pools the runner is a member of, by name; [] for none.
Pools []RunnerPoolRef `json:"pools"`
}
RunnerView is a runner as the management routes show it to a person or an administrator. It is never sent to a runner. Every posture fact is what the runner said about itself.
type SSHPublicKey ¶ added in v0.5.0
type SSHPublicKey struct {
Fingerprint string `json:"fingerprint"`
Principal string `json:"principal"`
Name string `json:"name"`
PublicKey string `json:"public_key"` // authorized_keys line; never a secret
Role string `json:"role"`
RoleCheckedAt *time.Time `json:"role_checked_at,omitempty"`
Capped bool `json:"capped"`
CreatedAt time.Time `json:"created_at"`
// RevSeq is the revocation sequence read when the key was registered; migration 0176 gave the keys
// that existed their order once. 0 only for a key written by a replica that predates it, which a
// revocation cutoff orders on CreatedAt. Never on the wire.
RevSeq int64 `json:"-"`
}
SSHPublicKey is a human's registered public key for the SSH gateway, distinct from GrantSpec's ssh_key grant (a resident private key for git-over-SSH — see GrantSSHKey). This is the gateway's trust root: it authenticates an incoming connection ONLY against rows here, then authorizes owner-or-admin. Fingerprint is computed server-side from the parsed key, never client-supplied. Role is the registering session's own role, stamped at registration and refreshed on every OIDC login — a BOUNDED-STALE stamp, not a live check: a demoted admin's key keeps its override until RoleCheckedAt exceeds WARDYN_SSH_ROLE_TTL, or re-registration; nil RoleCheckedAt reads as infinitely stale. Capped marks a key registered while an admin's session was in the user view: Role stays user through every re-stamp, and the admin override is never granted.
type ServiceIdentity ¶ added in v0.9.0
type ServiceIdentity struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Sponsor string `json:"sponsor"`
SponsorSince time.Time `json:"sponsor_since"`
UserType string `json:"user_type"`
CreatedBy string `json:"created_by"`
CreatedAt time.Time `json:"created_at"`
SuspendedAt *time.Time `json:"suspended_at,omitempty"`
SuspendedBy string `json:"suspended_by,omitempty"`
RetiredAt *time.Time `json:"retired_at,omitempty"`
RetiredBy string `json:"retired_by,omitempty"`
State ServiceIdentityState `json:"state"`
}
ServiceIdentity is a named, sponsored non-human principal (migration 0157, #1508). Its principal is ServicePrincipalPrefix + Name, and its tokens are ordinary api_tokens rows.
func (ServiceIdentity) BaseState ¶ added in v0.9.0
func (s ServiceIdentity) BaseState() ServiceIdentityState
BaseState is the state the record alone gives: retired, suspended or active. A sponsor whose session is not live makes an active identity sponsor_inactive, which only the API layer can read.
func (ServiceIdentity) Principal ¶ added in v0.9.0
func (s ServiceIdentity) Principal() string
Principal is the identity's principal, "service:<name>".
func (ServiceIdentity) Usable ¶ added in v0.9.0
func (s ServiceIdentity) Usable() bool
Usable reports whether the record alone lets a token authenticate: neither suspended nor retired. The sponsor's session is a second, separate check.
type ServiceIdentityState ¶ added in v0.9.0
type ServiceIdentityState string
ServiceIdentityState is where a service identity stands. It is derived from the row and the sponsor's session, never stored.
const ( ServiceIdentityActive ServiceIdentityState = "active" ServiceIdentitySuspended ServiceIdentityState = "suspended" ServiceIdentityRetired ServiceIdentityState = "retired" ServiceIdentitySponsorInactive ServiceIdentityState = "sponsor_inactive" )
type SiteBranding ¶ added in v0.8.4
type SiteBranding struct {
LogoPath string `json:"logo_path,omitempty"`
}
SiteBranding is SiteConfig.Branding. LogoPath is an absolute path on the wardynd host to an SVG or PNG file; it is read, checked as a console upload is, and stored as the branding logo at every apply. Naming it makes the file the logo's owner: the console offers no Remove logo while it is set. Taking it out of the document (and applying) removes the logo it delivered.
type SiteConfig ¶
type SiteConfig struct {
// Runners defaults off. PUT /site-config retains the stored value; the
// dedicated audited runner enablement operation owns changes.
Runners *RunnerSettings `json:"runners,omitempty"`
// UpstreamProxySecretRef names a secret holding the corporate upstream
// proxy URL, or "" for none. UpstreamProxyURL wins if both are set (not
// rejected, so an operator can migrate between the two).
UpstreamProxySecretRef string `json:"upstream_proxy_secret_ref,omitempty"`
// UpstreamProxyURL is the corporate upstream proxy URL written IN THE
// CLEAR (http only) — topology, not a credential. MUST NOT embed
// userinfo (validateSiteConfig rejects that).
UpstreamProxyURL string `json:"upstream_proxy_url,omitempty"`
// UpstreamProxyNoProxy is the upstream proxy's BYPASS list (like
// NO_PROXY): destinations wardyn-proxy dials DIRECTLY, matched by
// host/domain-suffix or CIDR. Exists for a private-endpoint estate whose
// corporate forward proxy has no route to the endpoint but this sidecar
// does: an entry is for a host the sidecar can itself resolve and reach.
// Where the corporate proxy is the only route, leave the host off this
// list and declare it in InternalHosts alone.
//
// SECURITY: a bypassed dial still hits the unconditional private/
// reserved-IP SSRF guard (InternalHosts is the only lift) and grants no
// policy allow. "*" is REFUSED at write time; empty (default) is
// unchanged behavior.
UpstreamProxyNoProxy []string `json:"upstream_proxy_no_proxy,omitempty"`
// ArtifactOverrides maps an ecosystem ("npm"|"pip"|"cargo"|"maven"|"go"|
// "nuget") to its corporate artifact-registry redirect.
//
// Deprecated: superseded by EgressRedirects; kept only so a
// pre-EgressRedirects document still decodes. handlePutSiteConfig and
// migration 0030 each fold a set value into EgressRedirects once.
ArtifactOverrides map[string]ArtifactOverride `json:"artifact_overrides,omitempty"`
// EgressRedirects is the operator's outbound redirect list: FROM a
// public/upstream URL or host TO a corporate-internal replacement.
// Ecosystem set gets the full behavior (config file + substitution +
// token injection); Ecosystem "" is network-only (no config file).
EgressRedirects []EgressRedirect `json:"egress_redirects,omitempty"`
// ScmHosts are the operator's default SCM hosts (e.g. "dev.azure.com",
// "github.example.com") the SCM Provider step / egress bundling consult.
ScmHosts []string `json:"scm_hosts,omitempty"`
// Integrations are the operator-configured external connections (AI
// providers, SCM hosts, generic connections), the generalized
// replacement the legacy fields above are migrating toward. Both are
// read; nothing removes the legacy fields yet.
Integrations IntegrationList `json:"integrations,omitempty"`
// InternalHosts declares an internal hostname the proxy's unconditional
// private/reserved-IP SSRF guard would otherwise refuse; each entry
// LIFTS the guard for matching addresses.
//
// SECURITY: leave CIDRs empty unless you know the sandbox's actual
// addresses — empty is the full liftable set, the safe default.
// Loopback/link-local/metadata/multicast/NAT64 stay denied regardless.
// This only lifts the SSRF builtin; policy still decides reachability.
// Admin-only, validated against ipguard.Liftable. Empty => no lift.
InternalHosts []InternalHost `json:"internal_hosts,omitempty"`
// Egress is the org's egress-grading policy. A POINTER for the same
// byte-identical-GET reason as the provider blocks. Written ONLY through
// PUT /governance/egress-baseline (a governance write); PUT /site-config
// carries an absent or unchanged block forward and refuses a changed one.
Egress *SiteEgress `json:"egress,omitempty"`
// WorkspaceProviders is the org's workspace-provider POLICY: which git
// hosts a run may clone from, with which credential lanes, plus storage
// ceilings. A POINTER since a value struct's omitempty is a no-op
// (would break the byte-identical-GET claim). Nil is legacy open mode;
// PUT /site-config's absent-key/explicit-{} makes it MDM-deliverable.
WorkspaceProviders *WorkspaceProviders `json:"workspace_providers,omitempty"`
// AgentProviders is the org's agent-enablement POLICY: which coding
// agents this deployment offers, each one's model-access lane, and
// whether that credential is shared or per-person. A POINTER for the
// same byte-identical-GET reason as WorkspaceProviders, with the same
// absent-key/explicit-{} write terms. Nil is legacy open mode.
AgentProviders *AgentProviders `json:"agent_providers,omitempty"`
// ModelProviders is the org's model-provider configuration — a POINTER
// for the same byte-identical-GET reason. Nil keeps today's
// lane-resolution path.
ModelProviders *ModelProviders `json:"model_providers,omitempty"`
// SignInHelpText and SignInHelpURL are the admin's "what to do next",
// shown on the sign-in page for refusals a person can't clear alone.
//
// SECURITY: PUBLIC — the anonymous /healthz publishes both, so they must
// never name internal systems. Text <=1,000 chars, no control chars;
// URL is http(s) only.
SignInHelpText string `json:"sign_in_help_text,omitempty"`
SignInHelpURL string `json:"sign_in_help_url,omitempty"`
// PolicyHelp is the contact a signed-in person is shown when a deployment-wide
// rule (no profile binds them) refuses them. A POINTER for the same
// byte-identical-GET reason as the provider blocks.
//
// NOT public: unlike the sign-in help pair, /healthz never publishes it.
// Validated by policyref.Validate at PUT; a stored value is re-validated by
// policyref.Project on every read.
PolicyHelp *policyref.Contact `json:"policy_help,omitempty"`
// Branding is the part of the console branding (#1215) a site config can
// carry: today only the logo, as a file wardynd reads when this document is
// applied. A POINTER for the same byte-identical-GET reason as the provider
// blocks; nil is "the site config says nothing about branding". Name and
// colours stay with the Branding card (PUT /branding/settings).
Branding *SiteBranding `json:"branding,omitempty"`
// Components is the org's custom-component POLICY. A POINTER for the same
// byte-identical-GET reason as the provider blocks. Nil — never set, or a
// document stored before the block existed — is the owner-decided default:
// no autonomy cap, env-var and file delivery allowed, no CC3 floor for
// component credentials.
Components *ComponentSettings `json:"components,omitempty"`
// EffectiveScmHosts is READ-ONLY, SERVER-OWNED: ScmHosts minus hosts a
// provider row claims, union enabled providers' hosts. A real field
// (not a wrapper key) so a get|set round trip still decodes under
// DisallowUnknownFields. PUT ignores a submitted value; never stored.
EffectiveScmHosts []string `json:"effective_scm_hosts,omitempty"`
// WithheldScmHosts is READ-ONLY, SERVER-OWNED, on the same terms as
// EffectiveScmHosts: each host a DISABLED provider row claims that
// EffectiveScmHosts therefore leaves out, with the row that withholds it, so
// an admin is told why a host is missing rather than left to look for it.
// Projected on read, ignored on PUT, never stored.
WithheldScmHosts []WithheldScmHost `json:"withheld_scm_hosts,omitempty"`
// OnboardingCompletedAt records when an operator finished (or left) the
// Getting Started funnel on THIS INSTALL. Nil until then.
//
// Server-side because a localStorage flag outlived a wiped database and
// was scoped per-origin (127.0.0.1 vs localhost disagreed).
//
// NOT settable via PUT /site-config — the handler ignores a submitted
// value and carries the stored one forward; a dropped value is reported
// as onboarding_completed_at_ignored.
OnboardingCompletedAt *time.Time `json:"onboarding_completed_at,omitempty"`
}
SiteConfig is the operator-wide, admin-authored baseline every run inherits: a corporate upstream proxy, egress redirects (package-registry mirrors and any outbound URL/host/IP redirect), and default SCM hosts. One singleton per operator; GetSiteConfig returns the zero value ("unconfigured") when none has been written yet.
SECURITY: secret VALUES never live here — only secret NAMES (refs) the broker/proxy resolve at dispatch/injection time, mirroring GrantSpec.Scope.
type SiteEgress ¶ added in v0.9.0
type SiteEgress struct {
// BaselineHosts are exact hostnames the composer grades as baseline egress,
// beside its built-in public set: an allowlist holding only these is not
// graded OPEN, and an api_key to one does not raise the CC3 floor.
//
// SECURITY: this LOWERS the egress grade and the confinement floor, so it is
// a governance write — security operators only, held for a second human where
// WARDYN_GOVERNANCE_SECOND_HUMAN is on, audited with the before/after sets,
// like the InternalHost.Baseline marks it shares one writer with.
// Exact lowercase hostnames only: no wildcard, scheme, port or IP. It grades;
// it never allows — a host must still pass allowed_domains.
BaselineHosts []string `json:"baseline_hosts,omitempty"`
}
SiteEgress is SiteConfig.Egress.
type Source ¶ added in v0.5.0
type Source struct {
ID uuid.UUID `json:"id"`
Kind SourceKind `json:"kind"`
// Locator is the identity: a host dir path (local_dir, trimmed) or
// canonical repo slug/clone URL (repo, lowercased); with Ref, unique in the store.
Locator string `json:"locator"`
// Ref is an optional git ref (repo only); part of identity, so the same repo at two refs is two sources with two contracts.
Ref string `json:"ref,omitempty"`
Name string `json:"name"`
Requirements map[string]WorkspaceRequirement `json:"requirements,omitempty"` // this source's own contract; see the type doc above
// Profile is this source's scan result, opaque here (never interpreted, only
// persisted/returned). json.RawMessage, not []byte: a bare []byte gets
// base64-encoded by encoding/json (WIRE-2), shipping it to GET /sources as
// an opaque string instead of real JSON.
Profile json.RawMessage `json:"profile,omitempty"`
Status WorkspaceStatus `json:"status"` // scan lifecycle: pending_scan | scanning | scanned | error
ActiveRunID *uuid.UUID `json:"active_run_id,omitempty"` // fences this source's in-flight scan run
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Source is one tier-1 library entry: a repo or dir configured once and attached to many workspaces. Requirements is what it expects of any sandbox using it, in Workspace.Requirements's "<type>:<key>" grammar, minus integration: keys (those compose only at the tier-3 aggregate).
type SourceKind ¶ added in v0.5.0
type SourceKind string
SourceKind is a library source's kind. Ephemeral scratch isn't a kind here: it's a per-workspace inline attachment, never a library entity.
const ( SourceLocalDir SourceKind = "local_dir" SourceRepo SourceKind = "repo" )
type StorageEnforcement ¶ added in v0.7.0
type StorageEnforcement string
StorageEnforcement names WHAT ACTUALLY BINDS BYTES for a drive, so a size nothing enforces is never rendered as a limit: "Wardyn never enforces a drive's size itself. On Kubernetes the size is the volume request and the storage class decides whether it binds. On Docker a managed drive has no byte cap. A share is bounded by its own quota. The size you see is the allocation, not a guarantee." Surfaced via runner.Capabilities.EphemeralDiskEnforcement on the admin setup status.
const ( // StorageEnforcementFilesystem: the filesystem itself refuses the write // (XFS project quota). NOTHING in v1 reports this yet. StorageEnforcementFilesystem StorageEnforcement = "filesystem" // StorageEnforcementEviction: the kubelet enforces it, MEASURED // PERIODICALLY — over the limit the POD IS KILLED, not the write; the // agent never sees ENOSPC and in-flight work is lost. StorageEnforcementEviction StorageEnforcement = "eviction" // StorageEnforcementRequest: the size is a scheduling REQUEST — a block // storage class binds it, an NFS/EFS provisioner enforces nothing. StorageEnforcementRequest StorageEnforcement = "request" StorageEnforcementExternal StorageEnforcement = "external" // something outside Wardyn binds it; Wardyn claims nothing StorageEnforcementNone StorageEnforcement = "none" // nothing binds it (e.g. a Docker named volume) )
func EnforcementFor ¶ added in v0.7.0
func EnforcementFor(b DriveBackend) StorageEnforcement
EnforcementFor maps a backend to what actually binds its bytes. An unknown backend reads as `none` — the one answer that cannot mislead an admin into trusting a cap.
type StorageProviders ¶ added in v0.7.2
type StorageProviders struct {
Ephemeral *EphemeralProvider `json:"ephemeral,omitempty"` // bounds writable scratch when a run mounts no drive
UserDrive *UserDriveProvider `json:"user_drive,omitempty"` // org switch and ceiling over user drives
}
StorageProviders is the file-system half of WorkspaceProviders; each nil sub-block is legacy behaviour for that half.
type TemplateAction ¶ added in v0.9.0
type TemplateAction string
TemplateAction is what a caller asks to do with a template.
const ( // TemplateRead is list, direct and versioned lookup, and use. TemplateRead TemplateAction = "read" // TemplateWrite is create, update, delete, and the target side of a copy // or publish. TemplateWrite TemplateAction = "write" )
type TemplateActor ¶ added in v0.9.0
type TemplateActor struct {
// Person is the authenticated principal's canonical subject.
Person string
// OrgAdmin is the current organisation-administrator tier (the one that
// writes the organisation's presets, policies and components).
OrgAdmin bool
// Groups is the verified group snapshot, each entry canonical. Nil means
// the snapshot is unavailable (a cookie from before groups were recorded).
Groups []string
// GroupsTruncated reports a snapshot that dropped entries or never arrived
// in full (cookie byte cap, an IdP groups overage, a token whose
// completeness was not recorded). Group authority cannot be answered from
// it, in either direction.
GroupsTruncated bool
// GroupAdminGrants are the groups the store holds a TemplateGroupAdmin
// grant for this person on.
GroupAdminGrants []string
}
TemplateActor is the caller, as the server verified them for this request.
type TemplateDenial ¶ added in v0.9.0
type TemplateDenial string
TemplateDenial is why TemplateAuthorize refused. The zero value is allowed.
const ( TemplateAllowed TemplateDenial = "" // TemplateDenyNotOwner: someone else's personal template. A read answers // it as a missing template, so the id is no existence oracle. TemplateDenyNotOwner TemplateDenial = "not_owner" // TemplateDenyNotOrgAdmin: org scope is written by an organisation administrator only. TemplateDenyNotOrgAdmin TemplateDenial = "not_org_admin" // TemplateDenyNotGroupMember: group scope is read by the group's members only. TemplateDenyNotGroupMember TemplateDenial = "not_group_member" // TemplateDenyNotGroupAdmin: group scope is written by that group's administrators only. TemplateDenyNotGroupAdmin TemplateDenial = "not_group_admin" // TemplateDenyGroupUnverified: the group snapshot is unavailable or // truncated, so membership and administration cannot be established. TemplateDenyGroupUnverified TemplateDenial = "group_unverified" // TemplateDenyBadOwner: the owner is malformed (a refusal, never a default). TemplateDenyBadOwner TemplateDenial = "bad_owner" )
func TemplateAuthorize ¶ added in v0.9.0
func TemplateAuthorize(actor TemplateActor, owner TemplateOwner, action TemplateAction) TemplateDenial
TemplateAuthorize decides one action on a template of the given owner.
- Person scope: the owner reads and writes; nobody else does, an organisation administrator included.
- Org scope: any signed-in person reads; an organisation administrator writes.
- Group scope: a verified member reads; a verified member who holds the group's template-management grant writes. A grant on group A says nothing about group B, and a grant outlives nothing: once the person leaves the group in the verified snapshot, they are no administrator of it either. An organisation administrator is not implicitly a group administrator; they delegate through the grant.
Group answers need a complete snapshot. Nil or truncated gives TemplateDenyGroupUnverified, never an empty-groups reading.
func TemplateCopyAuthorize ¶ added in v0.9.0
func TemplateCopyAuthorize(actor TemplateActor, from, to TemplateOwner) TemplateDenial
TemplateCopyAuthorize decides copying or publishing a template from one owner to another: the caller must read the source and write the target. Owning a personal template does not by itself let its content reach a wider audience; the store still re-checks every reference the copy discloses.
type TemplateGroupAdmin ¶ added in v0.9.0
type TemplateGroupAdmin struct {
Group string `json:"group_id"`
Person string `json:"person"`
GrantedBy string `json:"granted_by,omitempty"`
// Label is the group's display label, set by an organisation
// administrator. Display only: authority follows Group.
Label string `json:"label,omitempty"`
}
TemplateGroupAdmin is a bounded template-management grant: Person may publish and edit templates for Group, and nothing else. An organisation administrator writes and revokes it through an audited route. It is not a general administrator role, and it is not implied by group membership.
func (TemplateGroupAdmin) Canonical ¶ added in v0.9.0
func (g TemplateGroupAdmin) Canonical() (TemplateGroupAdmin, bool)
Canonical returns the grant in the form TemplateAuthorize compares: the group as the verified snapshot spells it (CanonicalGroupSubject) and the person as every user-subject boundary stores it (CanonicalUserSubject). ok is false for a group no snapshot could carry or an empty person, which a writer must refuse. The admin writer stores only what this returns, so a grant written as "DevTeam" authorises exactly the group the snapshot calls "devteam".
type TemplateKind ¶ added in v0.9.0
type TemplateKind string
TemplateKind is what a template holds. A policy is a template whose intent holds only run-wide network, credential and access-rule keys under inline_policy, laid onto the user's defaults at use.
const ( TemplateKindTemplate TemplateKind = "template" TemplateKindPolicy TemplateKind = "policy" )
func (TemplateKind) Valid ¶ added in v0.9.0
func (k TemplateKind) Valid() bool
Valid reports whether k is one of the two kinds.
type TemplateOwner ¶ added in v0.9.0
type TemplateOwner struct {
Scope TemplateScope `json:"scope"`
// Person is the owning person's canonical subject; set for person scope only.
Person string `json:"owner_id,omitempty"`
// Group is the canonical group subject the authenticated group snapshot
// carries (CanonicalGroupSubject); set for group scope only. Authority
// follows this identifier, never a display name.
Group string `json:"group_id,omitempty"`
}
TemplateOwner is the trusted catalogue key of one template: the scope and the identity that scope is bound to. The server assigns it from the authenticated request. A template document never carries one, so imported text cannot name its own owner or audience.
func (TemplateOwner) Valid ¶ added in v0.9.0
func (o TemplateOwner) Valid() bool
Valid reports whether the owner is well formed for its scope: person scope names a person and no group, group scope a group and no person, org scope neither.
type TemplateRoute ¶ added in v0.9.0
type TemplateRoute struct {
Route string
RouterClass string
Person TemplateTier
Org TemplateTier
Group TemplateTier
}
TemplateRoute classifies one template route: the router tier that gates reaching the handler, and the per-scope authority the handler then enforces with TemplateAuthorize. RouterClass uses authz_test.go's route class names and TestTemplateRoutesMatchRouteMatrix holds each row to routeMatrix and the router.
type TemplateScope ¶ added in v0.9.0
type TemplateScope string
TemplateScope is who a template is for.
const ( // TemplateScopePerson is private to the person who owns it. TemplateScopePerson TemplateScope = "person" // TemplateScopeOrg is offered to everyone signed in to the organisation. TemplateScopeOrg TemplateScope = "org" // TemplateScopeGroup is offered to the members of one group. TemplateScopeGroup TemplateScope = "group" )
func (TemplateScope) Valid ¶ added in v0.9.0
func (s TemplateScope) Valid() bool
Valid reports whether s is one of the three scopes.
type TemplateTier ¶ added in v0.9.0
type TemplateTier string
TemplateTier is the authority a template route asks for, per scope.
const ( // TemplateTierNone: the route does not act on that scope. TemplateTierNone TemplateTier = "" // TemplateTierOwner: the person who owns the template. TemplateTierOwner TemplateTier = "owner" // TemplateTierMember: any signed-in person. TemplateTierMember TemplateTier = "member" // TemplateTierOrgAdmin: an organisation administrator. TemplateTierOrgAdmin TemplateTier = "org_admin" // TemplateTierGroupMember: a verified member of that group. TemplateTierGroupMember TemplateTier = "group_member" // TemplateTierGroupAdmin: a verified administrator of that group, and of no other. TemplateTierGroupAdmin TemplateTier = "group_admin" )
type TokenDisposition ¶ added in v0.9.0
type TokenDisposition string
TokenDisposition is where an admin-minted token stands (a token whose minter is not its owner). It is derived at read from the token, the invitations that name it and its move row, never stored (#1508).
const ( // TokenNeedsDecision: live, with no pending invitation replacing it and no move. Nothing retires it. TokenNeedsDecision TokenDisposition = "needs_decision" // TokenAwaitingOwner: live, and a pending invitation to its owner names it as the token it replaces. TokenAwaitingOwner TokenDisposition = "awaiting_owner" // TokenReplacedByOwner: revoked by the claim of the invitation that replaced it. TokenReplacedByOwner TokenDisposition = "replaced_by_owner" // TokenLinkedToService: live, and an admin linked it to the service identity that replaces it. TokenLinkedToService TokenDisposition = "linked_to_service_identity" // TokenMovedToService: revoked, with a move row. TokenMovedToService TokenDisposition = "moved_to_service_identity" // TokenRevoked: revoked, with neither a claim nor a move behind it. TokenRevoked TokenDisposition = "revoked" )
type TokenMove ¶ added in v0.9.0
type TokenMove struct {
TokenID uuid.UUID `json:"token_id"`
ServiceIdentity string `json:"service_identity"`
LinkedBy string `json:"linked_by"`
LinkedAt time.Time `json:"linked_at"`
}
TokenMove is the row POST /tokens/{id}/move writes: an admin-minted token linked to the service identity that replaces it. The token stays live until an admin revokes it.
type TokenMoved ¶ added in v0.9.0
type TokenMoved struct {
TokenMove
Disposition TokenDisposition `json:"disposition"`
}
TokenMoved is the answer to POST /tokens/{id}/move.
type ToolEffect ¶ added in v0.7.0
type ToolEffect string
ToolEffect is what a matching ToolRule does with the call.
const ( // ToolAllow runs the call with no human in the loop. It is still recorded. ToolAllow ToolEffect = "allow" // ToolHold raises an approval and blocks — today's behaviour for every call. ToolHold ToolEffect = "hold" // ToolDeny refuses the call without asking anyone. ToolDeny ToolEffect = "deny" )
func ToolEffectForHarness ¶ added in v0.9.0
func ToolEffectForHarness(sets []HarnessToolRules, harness, tool string) (ToolEffect, bool)
ToolEffectForHarness reports what the rules of ONE harness say about a tool call, and whether any rule decided it. Match order: the exact tool, then that harness's own default. Another harness's rules never apply, and a harness with no entry has no rules (ok=false), so its call is held for a human exactly as a run with no rules is today; it never inherits a sibling's allow.
type ToolRule ¶ added in v0.7.0
type ToolRule struct {
Tool string `json:"tool"`
Effect ToolEffect `json:"effect"`
}
ToolRule is one entry in ToolRules: a tool NAME and what to do with it. Tool is matched exactly and case-sensitively against the name the harness reports (e.g. "Read", "Bash", "WebFetch") — deliberately not a pattern, since a glob invites a rule that reads narrower than it matches, and the set of tools a harness exposes is small and enumerable. The one wildcard is the literal "*", which sets the default for unmatched tools.
type UIApp ¶ added in v0.6.0
type UIApp struct {
Name string `json:"name"`
Port int `json:"port"`
Path string `json:"path,omitempty"`
}
UIApp is one declared in-sandbox HTTP app the UI gateway may relay to (RunPolicySpec.UIApps). Three fields, no command string: Name identifies the app to the operator and the launcher convention (/usr/local/bin/wardyn-ui-<name>), constrained to a short lower-case slug since it reaches a filesystem path and a URL query (never a slash, space, or ".."); Port is the port the app listens on INSIDE the sandbox, on 127.0.0.1 (the sandbox has no other reachable address); Path is where the app's UI lives, the landing path after the gateway's ticket handoff — empty means "/".
func (UIApp) PathOrRoot ¶ added in v0.6.0
PathOrRoot is Path with the empty default applied.
type UserDrive ¶ added in v0.7.0
type UserDrive struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Backend DriveBackend `json:"backend"`
// HostRoot is the operator-mounted tree a host_path drive binds a
// subdirectory of; empty for every other backend. Authored by an admin
// and bound into OTHER PEOPLE's sandboxes, so the security ceiling is the
// env allowlist (UserDriveHostRootCheck), never this column alone.
HostRoot string `json:"host_root,omitempty"`
StorageClass string `json:"storage_class,omitempty"` // k8s_pvc provisioner to request; "" = cluster default
HomeTemplate HomeTemplate `json:"home_template"`
// ObjectScheme names which half of a minted object name carries the
// drive — see DriveObjectScheme. NOT client-authored: on the wire only so
// a GET and round-tripped PUT agree.
ObjectScheme DriveObjectScheme `json:"object_scheme,omitempty"`
// SizeMiB is the ALLOCATION, not a guarantee — see StorageEnforcement. 0
// means "no allocation shown" except on k8s_pvc, where it IS
// requests.storage.
SizeMiB int `json:"size_mib,omitempty"`
Writable bool `json:"writable,omitempty"`
Reclaim DriveReclaim `json:"reclaim"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by,omitempty"`
// OwnerSubject is set only on a member drive: the canonical subject of the
// one person who owns it. Written once on create and never decoded from a
// request; "" on every admin row.
OwnerSubject string `json:"owner,omitempty"`
// DeleteStartedAt is a member drive's delete tombstone: while set, the
// drive is being destroyed and is treated as absent.
DeleteStartedAt *time.Time `json:"-"`
}
UserDrive is one drive (migration 0054's user_drives row), admin-registered unless OwnerSubject is set. Name is the human handle, unique among admin drives and per owner among member drives: what an admin allocates by, what the console lists, and the last tie-break in the resolver's ORDER BY. Writable defaults to FALSE; a grant or run may narrow it, never widen it.
type UserDriveGrant ¶ added in v0.7.0
type UserDriveGrant struct {
ID uuid.UUID `json:"id"`
SubjectType CapabilitySubjectType `json:"subject_type"`
Subject string `json:"subject"`
DriveID uuid.UUID `json:"drive_id"`
Priority int `json:"priority"` // breaks ties WITHIN a tier (higher wins); does NOT cross tiers
SizeMiBOverride int `json:"size_mib_override,omitempty"` // 0 means "use the drive's"
// WritableOverride is TRI-STATE: nil is "use the drive's posture",
// explicit false is "read only even on a writable drive".
WritableOverride *bool `json:"writable_override,omitempty"`
// HomeOverride names this subject's directory verbatim. USER TIER ONLY:
// on a group/all row it would give every member ONE home.
HomeOverride string `json:"home_override,omitempty"`
// Enabled is the admin's off switch. A disabled row still wins its tier:
// the winner's bit folds to ResolvedDrive.Paused rather than silently
// falling through to the wider row beneath it.
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"`
CreatedBy string `json:"created_by,omitempty"`
}
UserDriveGrant allocates one drive to one subject (migration 0054's user_drive_grants row). SubjectType REUSES CapabilitySubjectType, the same vocabulary capability_grants/governance_assignments use. Every override is a column on the BINDING row, since it has no meaning apart from the (subject, drive) pair.
type UserDriveHostRootCheck ¶ added in v0.7.0
UserDriveHostRootCheck is the signature of the deployment's ENV CEILING over admin-authored host_path drives (WARDYN_USER_DRIVE_HOST_ROOTS), returning nil when hostRoot is inside an allowed root.
A hook rather than a rule inside ValidateUserDrive: this package cannot read the environment, and site config (a full-replace row) is the wrong home for a security ceiling. An UNSET env means no host_path drive may be authored at all.
type UserDriveListItem ¶ added in v0.7.0
UserDriveListItem is one row of the admin Drives table. GrantCount is a READ-SIDE derivation kept off UserDrive so a GET/PUT round-trip cannot resurrect a stale count — what makes the delete-if-no-grants check honest.
type UserDriveProvider ¶ added in v0.7.2
type UserDriveProvider struct {
Disabled bool `json:"disabled,omitempty"` // turns drives off deployment-wide, negative-sense like GitProvider.Disabled, so zero value is today
MaxSizeMiB int `json:"max_size_mib,omitempty"` // largest drive allowed, refused at admin write boundary and clamped at resolve; 0 = no ceiling
// MemberCreate is the switch for people creating their own drives. nil = off.
MemberCreate *MemberDrivePolicy `json:"member_create,omitempty"`
}
UserDriveProvider is the org-level user-drive switch and ceiling. Disabled is a different question from GovernanceLimits.DenyUserDrive; the two are checked in order: this org switch first (422, refused-backend), then the per-profile door (403 + authz.denied).
type UserType ¶ added in v0.8.0
type UserType struct {
// ID is the immutable slug role-map values, grant subjects and token
// stamps refer to.
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
// Priority breaks a sign-in tie between two custom types (higher wins).
// Always 0 on the built-in type, which never takes part in a tie.
Priority int `json:"priority"`
BuiltIn bool `json:"built_in"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
CreatedBy string `json:"created_by,omitempty"`
}
UserType is one row of the user_types table: an org-defined kind of person ("Portfolio manager"). It carries no controls of its own — governance assignments, capability grants and drive grants name it as a subject.
type WithheldScmHost ¶ added in v0.8.4
type WithheldScmHost struct {
Host string `json:"host"`
ProviderID string `json:"provider_id"`
ProviderKind GitProviderKind `json:"provider_kind"`
}
WithheldScmHost is one entry of SiteConfig.WithheldScmHosts: a host kept out of the effective set because the named provider row is turned off. The row is named the way a refused clone names it, by kind and id.
type Workspace ¶
type Workspace struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
// Sources is the composition: one-or-more local_dir/repo/ephemeral entries.
Sources []WorkspaceSource `json:"sources"`
// BaseImage is the base-image choice; nil means the platform default
// convention image for the detected/scanned stack.
BaseImage *WorkspaceBaseImage `json:"base_image,omitempty"`
// Requirements is the declared requirements contract, keyed by a fixed
// grammar of "<type>:<key>":
//
// secret:NAME a named secret must be resolvable, e.g. "secret:acme-anthropic-key"
// egress:host the host must be reachable (bare host or "*."-wildcard), e.g. "egress:api.github.com"
// write:/host/path the HOST path must be mounted writable, e.g. "write:/home/user/repo"
// integration:ID the named Integration must be granted, e.g. "integration:git_host:github.com"
//
// Split on the FIRST colon only, since a write:/integration: key may itself
// contain colons. An overlay on attached sources' own contracts;
// EffectiveRequirements is the folded result a run consumes.
Requirements map[string]WorkspaceRequirement `json:"requirements,omitempty"`
// Attachments is the tier-3 composition: ordered source refs and inline
// ephemeral rows with per-attachment target/writable/overrides. Empty on
// a pre-split row, falling back to Sources.
Attachments []WorkspaceAttachment `json:"attachments,omitempty"`
// BaseImageID references the shared base-image catalog (tier 2); nil means
// "recommended" (per-workspace derived build) by design, not absence.
BaseImageID *uuid.UUID `json:"base_image_id,omitempty"`
// EffectiveRequirements is derived, read-only, never persisted: the
// FoldWorkspaceContract result over attachments + source contracts + the
// overlay above (equals Requirements on a pre-split row).
EffectiveRequirements map[string]WorkspaceRequirement `json:"effective_requirements,omitempty"`
// Kind mirrors Sources[0].Type (single-source only).
Kind WorkspaceKind `json:"kind"`
// Source mirrors Sources[0]'s host path (local_dir) or repo slug/URL (repo, single-source only).
Source string `json:"source"`
// Ref mirrors Sources[0].Ref (single-source repo only).
Ref string `json:"ref,omitempty"`
// DefaultTarget mirrors Sources[0].Target (single-source only).
DefaultTarget string `json:"default_target,omitempty"`
// Profile is internal/workspacescan's WorkspaceProfile, opaque here; nil/empty until scanned.
Profile json.RawMessage `json:"profile,omitempty"`
// ImageRef is the resolved/generated image for this workspace's profile; empty until scanned/built.
ImageRef string `json:"image_ref,omitempty"`
// BuiltProfileHash is the profile hash ImageRef was built from, the
// build-once/reuse-many cache key (rebuild only when it changes).
BuiltProfileHash string `json:"built_profile_hash,omitempty"`
// ImageBuild records what built ImageRef; written only by SetWorkspaceBuiltImage.
ImageBuild *ImageBuild `json:"-"`
// ApprovedEgress is the operator-owned list of egress hosts explicitly
// promoted, unioned into a run's allowlist. Never written by a scan;
// cleared when the composition changes.
ApprovedEgress []string `json:"approved_egress,omitempty"`
// DeniedEgress mirrors ApprovedEgress: hosts explicitly blocked, folded
// into denied_domains at create (deny beats allow/allow_all_egress/a
// runtime approval alike). Unlike ApprovedEgress, not cleared on source
// change (migration 0040) — a deny needs no re-review.
DeniedEgress []string `json:"denied_egress,omitempty"`
// EgressEditedAt is when an operator last REPLACED one of the two lists
// above — read by the boot heal, which without it would let a decided
// `always` approval re-widen a just-removed host every restart. Stamped
// only by those two scoped setters.
EgressEditedAt *time.Time `json:"egress_edited_at,omitempty"`
// ActiveRunID is the in-flight scan/record run, so the import panel can
// poll "is my step still running" without scanning all runs.
ActiveRunID *uuid.UUID `json:"active_run_id,omitempty"`
// RecordResults is the per-task Record Mode state; opaque
// map[string]api-owned JSON. Written only via SetWorkspaceRecordResult;
// cleared on composition change.
RecordResults json.RawMessage `json:"record_results,omitempty"`
// LLMCred is the model/harness credential binding a run inherits (see
// WorkspaceLLMCred). Written only via the scoped SetWorkspaceLLMCred.
LLMCred *WorkspaceLLMCred `json:"llm_cred,omitempty"`
// OwnedBy is the member who created this workspace (lowercased OIDC sub or
// email); empty means operator-owned. TRUST BOUNDARY: ownsWorkspaceOrAdmin
// keys on non-empty — only that member or an admin may read/write it, else
// the same 404 as a missing workspace. Set only by handleCreateWorkspace.
OwnedBy string `json:"owned_by,omitempty"`
Status WorkspaceStatus `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// AvailableToYou (#1267): per-caller bit — would the same decide path a
// launch runs admit THIS workspace for this reader? Computed fresh per GET
// (workspaceAvailableToCaller), never stored or accepted on write; always
// true for an operator. Pointer with omitempty: only the two GET stampers
// set it, so a plain bool would ship a false "unavailable" elsewhere — the
// console's "absent means fall back" contract needs the omitted key.
AvailableToYou *bool `json:"available_to_you,omitempty"`
}
Workspace is an onboarded, admin-reviewed composition of one-or-more sources (local_dir | repo | ephemeral — the ephemeral floor means Sources is never empty), a base image choice, and a requirements contract. Import scans the composition once into an opaque profile (internal/workspacescan WorkspaceProfile); runs thereafter reference the workspace by id. Profile/BuiltProfileHash are empty until Status leaves pending_scan.
type WorkspaceAttachment ¶ added in v0.5.0
type WorkspaceAttachment struct {
SourceID *uuid.UUID `json:"source_id,omitempty"`
Ephemeral bool `json:"ephemeral,omitempty"` // marks an inline scratch row (never a library entity)
Target string `json:"target,omitempty"` // in-sandbox mount/clone/scratch path for this attachment
Writable bool `json:"writable,omitempty"` // opts a local_dir attachment into read-write, per-attachment not per-source
// Overrides is this workspace's stance on requirement keys the attached
// source declares: reqKey -> off|optional|required. Never edits the
// shared source; keys the source doesn't declare are ignored harmlessly.
Overrides map[string]string `json:"overrides,omitempty"`
}
WorkspaceAttachment is one tier-3 composition row: either a library source (SourceID set) or an inline ephemeral row (Ephemeral true, SourceID nil); order is load-bearing, attachments[0] is the primary.
type WorkspaceBaseImage ¶ added in v0.5.0
type WorkspaceBaseImage struct {
// Kind is "recommended" (convention image for the detected stack),
// "registry" (operator-picked, named by Image), "custom" (Image is a base,
// see Steps), or "byo" (Image used verbatim).
Kind string `json:"kind"`
// Image is the base image reference. Meaning depends on Kind: the FROM for
// "custom", the image itself for "registry"/"byo", unused for "recommended".
Image string `json:"image,omitempty"`
// Steps is CATALOG IDENTITY ONLY, never applied to an image. SECURITY:
// running operator-authored RUN lines would be host-side RCE
// (assertWrapSafeBase also refuses ONBUILD), so "custom" builds
// identically to "byo". Must stay: part of base_images' UNIQUE(kind, image)
// index; removing it mints duplicate rows.
Steps []string `json:"steps,omitempty"`
}
WorkspaceBaseImage is a Workspace's base-image choice: what the sandbox's container image is built FROM, independent of its Sources.
type WorkspaceKind ¶
type WorkspaceKind string
WorkspaceKind discriminates an onboarded Workspace's legacy, single-source shape; a DERIVED READ-ONLY value mirroring Sources[0] (see Workspace.Kind) — a multi-source Workspace has no single Kind.
const ( WorkspaceKindLocalDir WorkspaceKind = "local_dir" WorkspaceKindRepo WorkspaceKind = "repo" // WorkspaceKindContainer was the pre-composition-model shape (base image, no // mount); migration 0029 rewrites every stored row into the composition // shape. Deprecated: kept as an anchor for 0029's backfill SQL and any // pre-0029 payload that still decodes "container". WorkspaceKindContainer WorkspaceKind = "container" )
type WorkspaceLLMCred ¶ added in v0.4.0
type WorkspaceLLMCred struct {
// ProviderRef pins a run to one model provider (by id). A disallowed or
// ineligible pin refuses the run rather than falling through to another
// provider.
ProviderRef string `json:"provider_ref,omitempty"`
// ProviderRef, for a caller "Available to" does not admit to the pinned
// provider: the workspace is pinned, to a provider they cannot use, and
// its id is not theirs to learn (#1018). A write carrying it is refused.
ProviderUnavailable bool `json:"provider_unavailable,omitempty"`
}
WorkspaceLLMCred is the operator-owned model binding on a workspace: a run that picks it uses the pinned model provider unless it names another. Nil or ProviderRef="" means no pin.
type WorkspaceMount ¶
type WorkspaceMount struct {
Source string `json:"source"`
Target string `json:"target"`
ReadOnly *bool `json:"read_only,omitempty"`
}
WorkspaceMount is one operator/policy-controlled host bind mount. Source is a host path (must be an absolute, cleaned path not under a denied location); Target is the in-container path (must be under an allowed prefix, e.g. /home/agent, /work, or /workspace). ReadOnly is a *bool so the safe default is read-only: omitted (nil) means read-only, and read-write requires the policy author to explicitly set "read_only": false (a plain bool would default to the unsafe direction). Use ReadOnlyOrDefault to resolve it.
func (WorkspaceMount) ReadOnlyOrDefault ¶
func (m WorkspaceMount) ReadOnlyOrDefault() bool
ReadOnlyOrDefault resolves the mount's effective read-only flag: an OMITTED (nil) read_only defaults to true (read-only), the safe default. Read-write is returned only when the policy explicitly set read_only=false.
type WorkspaceProviders ¶ added in v0.7.2
type WorkspaceProviders struct {
// Git rows: hosts a run may clone from and the credential lanes usable there.
Git []GitProvider `json:"git,omitempty"`
// Storage is the file-system half. A nil block is legacy behaviour.
Storage *StorageProviders `json:"storage,omitempty"`
// GitPatBrokerEnabled is read-only and server-owned: projected from
// WARDYN_GIT_PAT_BROKER on every GET, never stored or accepted on a PUT.
// A pointer since nil ("never projected") differs from a present false.
GitPatBrokerEnabled *bool `json:"git_pat_broker_enabled,omitempty"`
}
WorkspaceProviders is the org's workspace-provider policy: what kinds of work a sandbox may be given and the ceilings it's held to. The zero value is legacy open mode (load-bearing for upgraded installs), never derived from legacy SiteConfig.ScmHosts; URL validation lives in internal/api.
func (*WorkspaceProviders) Empty ¶ added in v0.7.2
func (p *WorkspaceProviders) Empty() bool
Empty reports whether p carries no policy — the shape a PUT uses to clear the block; the write boundary normalizes an empty block to nil.
type WorkspaceRepo ¶
type WorkspaceRepo struct {
Repo string `json:"repo"`
Target string `json:"target,omitempty"`
Ref string `json:"ref,omitempty"`
}
WorkspaceRepo is one operator/policy-controlled git repo attached to a run, paralleling WorkspaceMount for git-cloned (rather than bind-mounted) sources. Repo is a slug/URL validated the same way the legacy single-repo AgentRun.Repo field is. Target is an optional in-container clone destination; empty defers to the ~/work/<name> convention (not resolved here). Ref is an optional git ref (branch/tag/sha); empty clones the remote's default branch. Carried as a 4th tab-separated field in WARDYN_REPOS for agent-run-lib.sh's clone_one.
type WorkspaceRequirement ¶ added in v0.5.0
type WorkspaceRequirement struct {
// Level is "required" (a run cannot proceed without it) or "optional"
// (a run may proceed without it, degraded).
Level string `json:"level"`
// Provenance is "scan_seeded" (the scanner detected the need) or
// "operator_set" (an operator declared it directly).
Provenance string `json:"provenance"`
}
WorkspaceRequirement is one entry in a Workspace's requirements contract, keyed by the grammar documented on Workspace.Requirements.
type WorkspaceSource ¶ added in v0.5.0
type WorkspaceSource struct {
Type WorkspaceSourceType `json:"type"`
// Path is the host directory path (local_dir only).
Path string `json:"path,omitempty"`
// Source is the repo slug/URL (repo only).
Source string `json:"source,omitempty"`
// Ref is an optional git ref (branch/tag/sha, repo only).
Ref string `json:"ref,omitempty"`
// Target is the mount/clone/scratch-dir path inside the sandbox.
Target string `json:"target,omitempty"`
// Writable opts a local_dir source into read-write (default false =
// read-only; a plain bool since false is already the safe default).
Writable bool `json:"writable,omitempty"`
// Overrides is this attachment's stance on requirement keys its source
// declares: reqKey -> off|optional|required (workspace_contract.go's
// AttachmentOverride* constants). nil means "say nothing" (server carries
// forward the prior override); an explicit map REPLACES it outright.
Overrides map[string]string `json:"overrides,omitempty"`
// Admitted is a server-owned read projection, never persisted or from a
// write body: does policy admit this clone URL today? A pointer so
// "no answer" (absent, no provider row) differs from "not admitted".
Admitted *bool `json:"admitted,omitempty"`
// CloneTarget is a server-owned read projection on a repo source, never
// persisted or from a write body: the absolute path the clone lands at by
// default (Target, else /home/agent/work/<name>). A run's own selection
// target, when it sets one, wins over it.
CloneTarget string `json:"clone_target,omitempty"`
}
WorkspaceSource is one entry in a Workspace's composition (multiples of the same Type allowed). Meaningful fields depend on Type:
local_dir Path (host path), Target, Writable repo Source (slug/URL), Ref, Target ephemeral Target only — no Path, no Source, never Writable
type WorkspaceSourceType ¶ added in v0.5.0
type WorkspaceSourceType string
WorkspaceSourceType discriminates one entry in a Workspace's composition.
const ( WorkspaceSourceTypeLocalDir WorkspaceSourceType = "local_dir" // binds a host directory WorkspaceSourceTypeRepo WorkspaceSourceType = "repo" // clones a git repo // WorkspaceSourceTypeEphemeral is a sandbox-lifetime scratch dir, the // composition floor ensuring "one-or-more sources" never means zero. WorkspaceSourceTypeEphemeral WorkspaceSourceType = "ephemeral" )
type WorkspaceStatus ¶
type WorkspaceStatus string
WorkspaceStatus is the onboarding/scan lifecycle of a Workspace: not-yet- scanned, mid-scan, scanned, or errored. Later import-pipeline stages are RETIRED (migration 0029 collapsed every row back to scanned/pending_scan).
const ( WorkspacePendingScan WorkspaceStatus = "pending_scan" // onboarded, not yet scanned WorkspaceScanning WorkspaceStatus = "scanning" // scan run in flight WorkspaceScanned WorkspaceStatus = "scanned" // profile derived, ready to use WorkspaceError WorkspaceStatus = "error" // last scan attempt failed )
Source Files
¶
- agent_provider.go
- approval_notify.go
- attention.go
- audit_retention.go
- branding.go
- component.go
- component_config_schema.go
- component_host.go
- delegate.go
- device.go
- git_pat_scope.go
- governance.go
- governance_change.go
- group_subject.go
- holds.go
- image_build.go
- invitation.go
- launch_preset.go
- lost_reason.go
- mint_wire.go
- model_provider.go
- overlay.go
- override_narrowing.go
- placement.go
- policy.go
- push_content.go
- push_rule_ado_server.go
- push_rule_entry.go
- remote_runner.go
- role_mapping.go
- run_mode.go
- runner.go
- runner_inventory.go
- runner_limits.go
- runner_pool.go
- runner_pool_limits.go
- runner_registration.go
- runner_registration_wire.go
- runner_retirement.go
- secret_name.go
- secret_sentinels.go
- service_identity.go
- site_config.go
- template.go
- token_move.go
- types.go
- types_capability.go
- ui_layout.go
- user_drive.go
- user_drive_home.go
- user_drive_validate.go
- user_type.go
- workspace.go
- workspace_contract.go
- workspace_provider.go