Documentation
¶
Overview ¶
Package policy defines Halos's harness-neutral policy model: the YAML documents an org keeps in its policy repo, and the in-memory form every other package (compiler, gateway, delivery, eval) consumes.
Index ¶
- Constants
- Variables
- func BypassToken(s string) string
- func CanonicalResource(u string) (string, bool)
- func CanonicalUser(id string) string
- func ChannelName(ring, exp, variant string) string
- func CheckCustomHeaders(v string) error
- func ClaimString(claims map[string]any, name string) (string, bool)
- func Compile(org *Org) ([]byte, error)
- func DefaultModelFor(provider string) string
- func GateMode(m string) string
- func GeneratedFor(rollout string) string
- func HasErrors(issues []Issue) bool
- func HasTokenShape(s string) bool
- func IntersectScope(a, b []string) []string
- func KindServes(kind, proto string) bool
- func KnownMetric(name string) bool
- func LooksSecret(v string) bool
- func MatchDomain(pattern, host string) bool
- func ParseBake(s string) (time.Duration, error)
- func ParseCardKey(pemData string) (any, error)
- func SecureAgentURL(s string) bool
- func ServesNatively(tool, kind string) bool
- func SimpleGatewayName(org string) string
- func SplitModel(id, def string) (provider, model string)
- func ToolModelFor(tool, provider, defaultID string) (string, bool)
- func UnitForbiddenEnv(k string) string
- func ValidName(s string) bool
- func ValidTicket(t string) error
- func ValidVersion(v string) bool
- func WritableRootProblem(r string, protected []string) string
- type ActiveFreeze
- type Actor
- type AgentActor
- type AgentLimits
- type AgentScope
- type Agents
- func (a *Agents) Actor(name string) *AgentActor
- func (a *Agents) AdmitChain(rootGroups []string, d *DelegationChain) error
- func (a *Agents) AllowsExperiments() bool
- func (a *Agents) ChainModelLimit(d *DelegationChain) []string
- func (a *Agents) DeniesTool(name string, hosted bool) bool
- func (a *Agents) Depth() int
- func (a *Agents) ModelLimit(actor *AgentActor) []string
- func (a *Agents) RemoteAgent(name string) *RemoteAgent
- func (a *Agents) TTL() time.Duration
- type AllowFinding
- type Approval
- type ApprovalRule
- type Artifact
- type ArtifactContent
- type ArtifactSignature
- type Axis
- type Budget
- type ChangeScope
- type ChangeTarget
- type CloudAgent
- type CloudAgents
- type CloudConcurrency
- type CloudIdentity
- type CloudNetwork
- type Credential
- type CredentialRef
- type DLP
- type DLPInjection
- type DLPPattern
- type DLPRule
- type DLPSecrets
- type Delegation
- type DelegationChain
- type Egress
- type EgressRules
- type Experiment
- type ExperimentType
- type ExtensionSet
- type Extensions
- type FieldSource
- type FreezeStatus
- type FreezeWindow
- type Gateway
- type GatewayAuth
- type GitSource
- type Governance
- type Grant
- type Guardrail
- type HarnessSpec
- type Hook
- type Hooks
- type Identity
- type ImportOptions
- type Issue
- type Kind
- type Loosening
- type MCP
- type MCPAuth
- type MCPFinding
- type MCPPackage
- type MCPPin
- type MCPRegistry
- type MCPServer
- type MCPTokenDecision
- type MCPTools
- type Marketplace
- type Membership
- type Meta
- type MetricDef
- type MetricGoal
- type Metrics
- type ModelChoice
- type ModelPrice
- type ModelRoute
- type Models
- type Org
- func (o *Org) AgentScopeFor(s Subject) (AgentScope, error)
- func (o *Org) AgentScopeIn(ring, unit string) (AgentScope, error)
- func (o *Org) ApprovalRules(target ChangeTarget) []ApprovalRule
- func (o *Org) CheckTicket(target ChangeTarget, ticket string) error
- func (o *Org) ClientExperiments(ring string) []*Experiment
- func (o *Org) CodeOwners(prefix string, defaults []string) ([]byte, error)
- func (o *Org) DecideMCPToken(s Subject, resource string) MCPTokenDecision
- func (o *Org) DecideRemote(s Subject, d *DelegationChain, name, target, skill string) RemoteDecision
- func (o *Org) EffectiveProfile(base *Profile, unit string) (*Profile, error)
- func (o *Org) ExplainEffective(profile, unit string) (*Profile, []FieldSource, error)
- func (o *Org) FindArtifact(name string) (Artifact, bool)
- func (o *Org) FreezeAt(t time.Time, target ChangeTarget) FreezeStatus
- func (o *Org) Loosenings(parent, child *Profile) []Loosening
- func (o *Org) ProfileReach(name string) []string
- func (o *Org) Quorum(target ChangeTarget, requester, ticket string, approvals []Approval) *QuorumResult
- func (o *Org) QuorumFor(res *Resource, target ChangeTarget, requester, ticket string, ...) *QuorumResult
- func (o *Org) ResolveProfile(name string) (*Profile, error)
- func (o *Org) ResolveRing(s Subject) *Ring
- func (o *Org) ResolveUnit(s Subject) *Unit
- func (o *Org) RingTarget(rings ...string) ChangeTarget
- func (o *Org) ScopeWithin(inner, outer RoleScope) bool
- func (o *Org) SubjectResource(s Subject) Resource
- func (o *Org) SubjectTarget(s Subject) ChangeTarget
- func (o *Org) Unit(name string) *Unit
- func (o *Org) UnitChain(name string) ([]*Unit, error)
- func (o *Org) Validate() []Issue
- func (o *Org) VendorAdminRing() string
- func (o *Org) WithMCPRegistry(p *Profile, ring, unit string) (*Profile, error)
- type Overlay
- type Permissions
- type Plugins
- type Profile
- type Provider
- type QuorumResult
- type RegistryServer
- type RemoteAgent
- type RemoteDecision
- type ResolvedArtifact
- type Resource
- type Ring
- type RoleBinding
- type RoleGroups
- type RoleScope
- type Rollout
- type RolloutBaseline
- type RolloutChange
- type RolloutGates
- type RolloutPreset
- type RolloutStep
- type RolloutStrategy
- type Root
- type RouteTarget
- type RuleResult
- type Sandbox
- type ScorecardGate
- type SelfService
- type ServerJSON
- type Severity
- type SignalSource
- type Simple
- type Source
- type StatusLine
- type Stopping
- type Subject
- type SupplyChain
- type Telemetry
- type Toggle
- type ToggleClient
- type TogglePatch
- type ToggleRule
- type ToggleTraffic
- type ToolSpec
- type TrustedIdentity
- type Unit
- type UnitMembers
- type Upstream
- type Variant
- type VendorAdmin
- type VendorTarget
- type WeeklyWindow
- type WireFitness
- type WorkloadIssuer
- type WorkloadRule
- type Workloads
- func (w *Workloads) AllowsExperiments() bool
- func (w *Workloads) AudienceFor(i WorkloadIssuer) string
- func (w *Workloads) IssuerByURL(iss string) (WorkloadIssuer, bool)
- func (w *Workloads) MatchWorkload(issuer string, claims map[string]any) (WorkloadRule, string, bool)
- func (w *Workloads) UnpinnedRules() []string
Constants ¶
const ( // AgentSubjectPrefix starts every actor's sub in a delegated credential's // act claim, so an actor can never be mistaken for a human or a workload. AgentSubjectPrefix = "agent:" // DefaultAgentMaxDepth is the delegation depth when agents.maxDepth is unset: // an agent, and one subagent it spawns. DefaultAgentMaxDepth = 2 // MaxAgentDepth is the hard ceiling: policy cannot raise maxDepth above it // and verifiers refuse longer chains whatever the policy says. MaxAgentDepth = 5 // DefaultAgentTTL is the delegated credential lifetime when // agents.maxTTLSeconds is unset. The ceiling is MaxWorkloadTTL. DefaultAgentTTL = 15 * time.Minute )
const ( ScopeUser = "user" ScopeGroup = "group" ScopeRing = "ring" ScopeOrg = "org" WindowDay = "day" WindowWeek = "week" WindowMonth = "month" WindowRolling = "rolling" BudgetWarn = "warn" BudgetEnforce = "enforce" )
Budget scopes, windows and actions.
const ( CloudClaude = "claude" CloudCodex = "codex" CloudCopilot = "copilot" CloudCursor = "cursor" CloudGemini = "gemini" CloudIdentityWorkload = "workload" NetworkNone = "none" NetworkAllowlist = "allowlist" NetworkFull = "full" OverLimitAlert = "alert" OverLimitPause = "pause" )
Cloud agent vendors, identity modes, network modes and over-limit actions.
const ( DLPMonitor = "monitor" DLPRedact = "redact" DLPBlock = "block" DLPOff = "off" )
DLP actions.
const ( DefaultDLPThreshold = 4 DefaultDLPMaxScanBytes = 4 << 20 )
DLP defaults and bounds.
const ( EgressAllow = "allow" EgressDeny = "deny" )
Egress defaults (ADR-0032).
const ( // MCPModeAllowlist (the default) renders the registry as each harness's // managed allowlist: servers outside it are blocked where the harness can // block them, and profiles, units and toggles may only name registry servers. MCPModeAllowlist = "allowlist" // MCPModeWarn renders the registry servers but blocks nothing else: halod // reports unapproved servers as drift. A migration step, not an end state. MCPModeWarn = "warn" )
Registry modes.
const ( MCPAuthNone = "none" // MCPAuthOAuth: the server's own authorization server (MCP authorization, // OAuth 2.1), with a pre-registered client id or a Client ID Metadata Document. MCPAuthOAuth = "oauth" // MCPAuthEnterprise: Enterprise-Managed Authorization (the // io.modelcontextprotocol/enterprise-managed-authorization extension): the // org IdP (identity.issuer) issues an ID-JAG the client trades at the // server's authorization server. MCPAuthEnterprise = "enterprise" // MCPAuthHalos: halo-server is the server's authorization server and mints // short-lived, audience-bound access tokens for it (POST /api/v1/token/exchange // with resource=<server url>). MCPAuthHalos = "halos" )
Authorization modes of a remote registry server (MCPAuth.Mode).
const ( MCPPackageNPM = "npm" MCPPackagePyPI = "pypi" MCPPackageOCI = "oci" MCPPackageBinary = "binary" )
Package types of a stdio registry server (MCPPackage.Type), as the official MCP Registry's server.json registryType names them (binary is Halos's).
const ( RolloutDraft = "draft" RolloutActive = "active" RolloutPaused = "paused" RolloutAborted = "aborted" RolloutCompleted = "completed" )
Rollout statuses. Only active rollouts are driven by the controller.
const ( SimpleProfile = "default" ProviderMulti = "multi" SafetyStrict = "strict" SafetyStandard = "standard" SafetyRelaxed = "relaxed" )
Simple-mode names. The profile every generated ring uses is SimpleProfile.
const ( ArtifactSkill = "skill" // a directory holding SKILL.md ArtifactPlugin = "plugin" // a plugin directory (.claude-plugin/plugin.json, ...) ArtifactHook = "hook" // hook scripts a hooks.items command runs ArtifactCommand = "command" // a slash command: one Markdown file ArtifactAgent = "agent" // a subagent definition: one Markdown file )
Artifact kinds for SupplyChain.Artifacts (ADR-0028).
const ( ScanOff = "off" ScanWarn = "warn" ScanEnforce = "enforce" )
Scan modes for SupplyChain.Scan.
const ( EffectOn = "on" EffectOff = "off" )
Toggle effects for ToggleRule.Effect.
const ( GroupsOverageDeny = "deny" GroupsOverageGraph = "graph" )
Identity.GroupsOverage values.
const ( MarketplaceGitHub = "github" // Repo (OWNER/REPO), optional Ref MarketplaceGit = "git" // URL, optional Ref MarketplaceURL = "url" // URL of a marketplace.json MarketplaceHostPattern = "hostPattern" // HostPattern, a regex over marketplace hosts )
Marketplace source kinds for Extensions.Marketplaces.
const ( LoginSubscription = "subscription" LoginAPI = "api" )
Login restrictions for Profile.Login.
const ( GateOff = "off" GateWarn = "warn" GateEnforce = "enforce" )
Gate modes for Ring.Posture and Ring.VersionGate.
const ( ShadowTurnsFirst = "first" ShadowTurnsAll = "all" )
Shadow turn scopes (Experiment.Turns).
const ( DefaultWorkloadTTL = 15 * time.Minute MaxWorkloadTTL = time.Hour )
Workload token lifetimes: halo-server never mints one longer than MaxWorkloadTTL; a rule without maxTTLSeconds gets DefaultWorkloadTTL.
const APIVersion = "halos.dev/v1"
APIVersion is the stable document version this build writes.
const APIVersionV1Alpha1 = "halos.dev/v1alpha1"
APIVersionV1Alpha1 is the pre-GA document version. It is still read, with semantics identical to APIVersion and a deprecation warning from Validate; `halo migrate` rewrites it. Removed only in halos.dev/v2 (see docs/reference/compatibility).
const EngineExternal = "external"
EngineExternal is a gateway Halos does not run.
const GitHubActionsProvider = "GitHub Actions"
GitHubActionsProvider is TenantClaims' provider name for the GitHub Actions OIDC issuer.
const IncidentFreezeName = "incident"
IncidentFreezeName names the incident freeze halo-server toggles.
const LabelGeneratedBy = "halos.dev/generated-by"
LabelGeneratedBy marks a document a tool generated for one owner, with the value GeneratedFor(rollout): `halo model switch --canary` labels the gateway.yaml override it writes, and the rollout's completion deletes it.
const MaxChannelLen = 100
MaxChannelLen bounds a delivery channel name. Channels become OCI tags ("ring-<channel>.pointer", tag limit 128) and halod state keys, so a name that would exceed this is shortened by hashing (see ChannelName).
const RootFile = "halos.yaml"
RootFile is the policy repo's root document.
const SnapshotVersion = "halos.dev/snapshot/v1"
SnapshotVersion identifies the envelope format written by Compile.
const SubjectWorkload = "workload"
SubjectWorkload is Subject.Kind for a CI / cloud workload: a principal halo-server minted from a federated OIDC token (ADR-0023), not a human.
const TestsDir = "tests"
TestsDir is the policy repo's top-level directory of PolicyTest documents (internal/policytest, `halo test`). Load does not parse it as policy.
const ToggleDateLayout = "2006-01-02"
ToggleDateLayout is the layout of Toggle.Expires.
const ToggleSaltPrefix = "halos/toggles/"
ToggleSaltPrefix namespaces percent-rollout hashing per toggle, so a toggle's cohort is independent of the ring and of every experiment (see internal/assign).
const WorkloadSubjectPrefix = "workload:"
WorkloadSubjectPrefix starts every workload subject id, so a workload can never take a human's id (ring membership, budgets, audit).
Variables ¶
var CloudVendors = []string{CloudClaude, CloudCodex, CloudCopilot, CloudCursor, CloudGemini}
CloudVendors lists the vendors in a fixed order.
var ConsoleRoles = []string{"viewer", "operator", "approver", "auditor", "admin"}
ConsoleRoles are halo-server's roles (ADR-0015), in display order. internal/server's Roles is this list.
var DLPDetectorIDs = []string{
"anthropic-api-key", "aws-access-key-id", "gcp-service-account", "generic-secret", "github-token",
"gitlab-token", "jwt", "openai-api-key", "private-key", "slack-token", "slack-webhook", "stripe-key",
}
DLPDetectorIDs are the built-in secret detector ids (internal/gateway owns the detectors; tests keep the two lists equal).
var DefaultAlertAt = []float64{0.8}
DefaultAlertAt is the soft threshold when a budget sets none.
var EgressPresets = map[string][]string{
"package-registries": {
"registry.npmjs.org", "pypi.org", "files.pythonhosted.org", "proxy.golang.org", "sum.golang.org",
"index.crates.io", "static.crates.io", "repo.maven.apache.org", "rubygems.org",
},
"vcs": {
"github.com", "api.github.com", "codeload.github.com", "*.githubusercontent.com",
"gitlab.com", "bitbucket.org",
},
"model-providers": {
"api.anthropic.com", "api.openai.com", "generativelanguage.googleapis.com",
"api.githubcopilot.com", "*.githubcopilot.com",
},
}
EgressPresets are the named domain sets egress.presets expands to. Each set is the hosts the named service documents for clients; see docs/guides/egress-and-sandbox.md for the source of every entry.
var ExtraGuardrails []Guardrail
ExtraGuardrails run after the built-in guardrails. They can only add issues: the built-ins have already been judged and recorded, so an extra can never remove or downgrade one (ADR-0007). Set once at start-up.
var MetricRegistry = []MetricDef{ {Name: "halo.task.success", Unit: "ratio", Description: "Fraction of tasks that ended successfully (eval verdict).", Sources: map[string]string{"claude-code": "halo eval trial verdict", "codex": "halo eval trial verdict", "gemini-cli": "halo eval trial verdict"}, Source: []SignalSource{SourceEval}, PerUnit: "one eval trial (pass = 1)"}, {Name: "halo.cost.usd_per_session", Unit: "usd", Description: "Model spend per session.", Sources: map[string]string{"claude-code": "claude_code.cost.usage", "codex": "codex.api_request events (tokens x price)", "gemini-cli": "gemini_cli.api_response (tokens x price)"}, Source: []SignalSource{SourceOnlineCLI}, PerUnit: "unit's total halo.cost.usd / its distinct sessions"}, {Name: "halo.tokens.per_session", Unit: "tokens", Description: "Input+output tokens per session.", Sources: map[string]string{"claude-code": "claude_code.token.usage", "codex": "codex.api_request events", "gemini-cli": "gemini_cli.token.usage"}, Source: []SignalSource{SourceOnlineCLI}, PerUnit: "unit's input+output halo.tokens / its distinct sessions"}, {Name: "halo.api.error_rate", Unit: "ratio", Description: "Fraction of model API requests that failed (non-2xx or stream error).", Sources: map[string]string{"claude-code": "claude_code.api_error / (claude_code.api_request + claude_code.api_error) events", "codex": "codex.api_request events (http.response.status_code, error.message)", "gemini-cli": "gemini_cli.api_error / (gemini_cli.api_response + gemini_cli.api_error)", "gateway": "halo.gateway.requests{status_class!=2xx} / halo.gateway.requests"}, Source: []SignalSource{SourceOnlineGateway, SourceOnlineCLI}, PerUnit: "unit's failed / total model API requests"}, {Name: "halo.edit.accept_rate", Unit: "ratio", Description: "Fraction of proposed code edits the user accepted.", Sources: map[string]string{"claude-code": "claude_code.code_edit_tool.decision", "codex": "codex.tool_decision events", "gemini-cli": "gemini_cli.tool_call (decision)"}, Source: []SignalSource{SourceOnlineCLI}, PerUnit: "unit's accepted / total edit decisions"}, {Name: "halo.latency.p50_ms", Unit: "ms", Description: "Median latency of successful model requests.", Sources: map[string]string{"claude-code": "claude_code.api_request duration_ms", "codex": "codex.api_request duration_ms (2xx)", "gemini-cli": "gemini_cli.api_response duration_ms", "gateway": "halo.gateway.latency_ms{status_class=2xx} histogram"}, Source: []SignalSource{SourceOnlineGateway, SourceOnlineCLI}, PerUnit: "median over the unit's successful requests (gateway: interpolated from histogram buckets)"}, {Name: "halo.latency.p95_ms", Unit: "ms", Description: "95th percentile latency of successful model requests.", Sources: map[string]string{"claude-code": "claude_code.api_request duration_ms", "codex": "codex.api_request duration_ms (2xx)", "gemini-cli": "gemini_cli.api_response duration_ms", "gateway": "halo.gateway.latency_ms{status_class=2xx} histogram"}, Source: []SignalSource{SourceOnlineGateway, SourceOnlineCLI}, PerUnit: "p95 over the unit's successful requests (gateway: interpolated from histogram buckets)"}, {Name: "halo.session.duration_s", Unit: "s", Description: "Wall-clock session length.", Sources: map[string]string{"claude-code": "claude_code.active_time.total", "codex": "codex.session events", "gemini-cli": "gemini_cli.session events"}, PerUnit: "not yet derived: no online source"}, {Name: "halo.tool.error_rate", Unit: "ratio", Description: "Fraction of tool calls that errored.", Sources: map[string]string{"claude-code": "claude_code.tool_result events (success)", "codex": "codex.tool_result events (success)", "gemini-cli": "gemini_cli.tool_call (success)"}, Source: []SignalSource{SourceOnlineCLI}, PerUnit: "unit's failed / total tool calls"}, {Name: "halo.eval.judge.score", Unit: "ratio", Description: "LLM-judge rubric score (0..1, higher is better) of a model response, graded by `halo eval online` on halo-shadow pairs.", Sources: map[string]string{"shadow": "halo eval online -> halo.eval.judge.score{halo.unit=<hashed session>}, halo.source=eval"}, Source: []SignalSource{SourceOnlineEval}, PerUnit: "mean judge score of one session's graded shadow pairs for that arm (unit = hashed session; a first-turn pair without a session is its own unit)", Better: "increase"}, {Name: "halo.eval.agent.fidelity", Unit: "ratio", Description: "Share of agent steps where the arm's next action (tools called) matches the action production took on the same real prefix; the control's value is the re-sampling noise floor (ADR-0014).", Sources: map[string]string{"shadow": "halo eval online -> halo.eval.agent.fidelity{halo.unit=<hashed session>}, halo.source=eval (shadow experiments with turns: all)"}, Source: []SignalSource{SourceOnlineEval}, PerUnit: "mean step fidelity over one session's referenced steps for that arm (unit = session)", Better: "increase"}, {Name: "halo.eval.agent.invalid_call_rate", Unit: "ratio", Description: "Share of agent steps where the arm called a tool the request did not offer or omitted a required argument (ADR-0014).", Sources: map[string]string{"shadow": "halo eval online -> halo.eval.agent.invalid_call_rate{halo.unit=<hashed session>}, halo.source=eval"}, Source: []SignalSource{SourceOnlineEval}, PerUnit: "share of one session's steps with an invalid call for that arm (unit = session)", Better: "decrease"}, }
MetricRegistry lists every metric experiments may reference; Validate rejects names not in this list.
var RolloutPresets = map[string]RolloutPreset{ "fast": {Name: "fast", TeamBake: "4h", RingBake: "1d", Rings: []Ring{presetRing("ring0-team", 0, Membership{}), presetRing("ring1-ga", 1, Membership{Default: true})}, Canary: []RolloutStep{canary(25, "4h", 100)}}, "standard": {Name: "standard", TeamBake: "1d", RingBake: "2d", Rings: []Ring{presetRing("ring0-team", 0, Membership{}), presetRing("ring1-canary", 1, Membership{Percent: 5}), presetRing("ring2-early", 2, Membership{Percent: 25}), presetRing("ring3-ga", 3, Membership{Default: true})}, Canary: []RolloutStep{canary(5, "12h", 200), canary(25, "1d", 500), canary(50, "1d", 1000)}}, "careful": {Name: "careful", TeamBake: "2d", RingBake: "3d", Rings: []Ring{presetRing("ring0-team", 0, Membership{}), presetRing("ring1-canary", 1, Membership{Percent: 1}), presetRing("ring2-early", 2, Membership{Percent: 5}), presetRing("ring3-broad", 3, Membership{Percent: 25}), presetRing("ring4-ga", 4, Membership{Default: true})}, Canary: []RolloutStep{canary(1, "1d", 200), canary(5, "2d", 500), canary(25, "2d", 2000), canary(50, "3d", 4000)}}, }
RolloutPresets are fast, standard and careful.
Functions ¶
func BypassToken ¶ added in v0.4.0
BypassToken returns the first permission-bypass token in s, or "". It is the single invariant 1 check: policy validation runs it on every string in the org, the release check on every byte of every rendered file, and vendoradmin and halo ci config on what they emit.
func CanonicalResource ¶ added in v0.4.0
CanonicalResource is u in the form MCP authorization compares resources in (RFC 8707 section 2): https, lowercase scheme and host, no default port, fragment, query or credentials, and no trailing slash. ok is false for anything that is not such a URL.
func CanonicalUser ¶ added in v0.3.0
CanonicalUser is the one form in which Halos compares or keys a human user id: halo-server's sessions, devices, enrollments, posture, SCIM userName, access-request self-approval and whoami, and the per-user budget counters (budget.Charges) that halo-server and the gateways share. It trims surrounding space and lowercases, so an IdP that provisions "Alice@Corp.com" and logs in "alice@corp.com" is one user.
It never merges a lookalike into another letter. strings.ToLower maps the Kelvin sign U+212A to "k", U+0130 (I with dot) to "i", the Ohm sign to omega and the Angstrom sign to a-ring, so "Kelly" would key the same sessions, devices and budget as "kelly". A rune is lowercased only when uppercasing the result gives it back (a real case pair: A/a, É/é, Σ/σ); compatibility characters are kept as they are, so a lookalike id stays a different user. This is the outcome of rejecting such ids without needing a refusal at every identity entry point. Not NFKC: compatibility normalisation would merge exactly these lookalikes (and fullwidth letters) into the ASCII ids they imitate.
func ChannelName ¶
ChannelName is the delivery channel that serves variant of client-axis experiment exp to devices in ring: "<ring>.x-<exp>.<variant>". When that exceeds MaxChannelLen the experiment/variant part is replaced by the first 20 hex chars of sha256(exp NUL variant). Names are ValidName, so the result only uses [a-z0-9._-] and fits the OCI tag grammar.
The ring release names its channels in its signed manifest; halod recomputes the name and refuses a mismatch. Validate rejects policies where a channel name collides with a ring or another channel.
func CheckCustomHeaders ¶ added in v0.2.0
CheckCustomHeaders validates a profile's newline-separated ANTHROPIC_CUSTOM_HEADERS: no CR (header injection) and no x-halo-* header, which only Halos sets.
func ClaimString ¶ added in v0.3.0
ClaimString renders a scalar claim (string, bool, number) as a string. Objects, arrays and absent claims report false.
func Compile ¶
Compile renders org as deterministic JSON (same input => same bytes: maps are key-sorted by encoding/json, no timestamps) for the gateway plane.
func DefaultModelFor ¶
DefaultModelFor is the model `halo init` gives the default alias on provider.
func GeneratedFor ¶
GeneratedFor is the LabelGeneratedBy value of documents generated for rollout.
func HasTokenShape ¶
HasTokenShape reports whether s contains a well-known credential shape (vendor token prefixes such as sk-ant-, ghp_, xoxb-; AWS access key ids; PEM private key blocks) outside ${VAR} references. It is the low-false- positive subset of looksSecret, safe to run over whole rendered files.
func IntersectScope ¶ added in v0.3.0
IntersectScope intersects two model scopes where nil means unrestricted. The result is nil only when both are nil; otherwise it is non-nil (possibly empty: nothing allowed), so an empty intersection can never widen to "all".
func KindServes ¶
KindServes reports whether an upstream of kind can serve a client speaking proto. Pass-through kinds (orchestrator, anthropic, openai, gemini) keep the client's wire as-is. bedrock additionally serves anthropic-messages clients (the request is translated and the event stream converted back); vertex serves only anthropic-messages; azure-openai only openai-responses; gemini clients are served only by kinds gemini and orchestrator.
func KnownMetric ¶
KnownMetric reports whether name is in MetricRegistry.
func LooksSecret ¶
LooksSecret reports whether v looks like a literal credential (see looksSecret).
func MatchDomain ¶ added in v0.4.0
MatchDomain reports whether host matches pattern: an exact host, or "*.example.com", which matches every subdomain of example.com but not example.com itself. Case and a trailing dot are ignored.
func ParseCardKey ¶ added in v0.3.0
ParseCardKey parses a PEM PUBLIC KEY usable for AgentCard JWS: ed25519, ECDSA P-256/P-384 or RSA >= 2048 bits.
func SecureAgentURL ¶ added in v0.3.0
SecureAgentURL reports whether s is an absolute https URL without credentials or fragment (an AgentCard or JWKS location).
func ServesNatively ¶
ServesNatively reports whether upstream provider kind answers tool's wire (true for tools the gateway does not route).
func SimpleGatewayName ¶
SimpleGatewayName is the generated Gateway document's name.
func SplitModel ¶
SplitModel splits "<provider>/<model>" when the prefix is a known provider (Bedrock ARNs keep their slashes); otherwise the model runs on def.
func ToolModelFor ¶
ToolModelFor reports whether tool needs its own start model because defaultID (the default alias's model, on provider) runs on a provider that cannot answer the tool's wire, and if so the vendor model to use ("<provider>/<model>"). Tools the gateway does not route (copilot-cli, cursor-agent) never need one.
func UnitForbiddenEnv ¶ added in v0.3.0
UnitForbiddenEnv reports why a unit may never set env key k ("" when it may): it would redirect model traffic, credentials or telemetry, or run code in the harness, its shell or the tools it calls (git, pagers, editors, compilers, language runtimes). Matching ignores case; families are prefixes.
func ValidName ¶
ValidName reports whether s is a safe policy identifier (ring, profile, experiment, variant, model alias, upstream, MCP server).
func ValidTicket ¶ added in v0.4.0
ValidTicket checks a ticket reference's shape: it lands in audit records and PR bodies, so it is a short token, never free text.
func ValidVersion ¶ added in v0.5.0
ValidVersion reports whether v is an exact semver, the only form a harness version pin may take (no ranges, no latest, no date-style versions).
func WritableRootProblem ¶ added in v0.4.0
WritableRootProblem is why r may not be a sandbox writable root next to protected ("" = fine): not absolute or ~/, a glob, the disk, a system directory or one of its ancestors, a home directory (as ~ or absolute), or overlapping a protected path. Paths compare case-insensitively (macOS and Windows file systems are), and an absolute path under a home directory (/Users/alice/.ssh) is also compared in its ~ form (~/.ssh). Shared by validation and the release check (invariant 1).
Types ¶
type ActiveFreeze ¶ added in v0.4.0
type ActiveFreeze struct {
Name string `json:"name"`
Reason string `json:"reason"`
// Until is the end of this occurrence; zero = until a human lifts it
// (incident freeze) or never (a window that does not parse: fail closed).
Until time.Time `json:"until,omitzero"`
Incident bool `json:"incident,omitempty"`
By string `json:"by,omitempty"`
}
ActiveFreeze is one window holding a change.
type Actor ¶ added in v0.3.0
type Actor struct {
// Agent is the agents.actors name the credential was minted for.
Agent string `json:"agent"`
// Harness and Session say where the agent runs (claude-code, codex, ...;
// the harness session id). Informational: recorded for audit, never routed on.
Harness string `json:"harness,omitempty"`
Session string `json:"session,omitempty"`
}
Actor is one link of a delegation chain.
type AgentActor ¶ added in v0.3.0
type AgentActor struct {
Name string `yaml:"name" json:"name"`
// Harnesses it may run in (claude-code, codex, ...); empty = any.
Harnesses []string `yaml:"harnesses,omitempty" json:"harnesses,omitempty"`
// Groups: the root principal must hold one of them; empty = any root.
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
// Models narrows agents.allowedModels for this agent; empty = no narrowing.
Models []string `yaml:"models,omitempty" json:"models,omitempty"`
}
AgentActor is one agent that may act for a principal.
type AgentLimits ¶ added in v0.3.0
type AgentLimits struct {
// MaxDepth lowers agents.maxDepth for the profile's members; 0 = the org's.
MaxDepth int `yaml:"maxDepth,omitempty" json:"maxDepth,omitempty"`
// Remote names the agents.remote entries members may call; empty = every
// entry whose rings or units admit them.
Remote []string `yaml:"remote,omitempty" json:"remote,omitempty"`
}
AgentLimits is a profile's (or unit overlay's) bound on delegated agents. A unit may only lower MaxDepth and narrow Remote (see Loosenings).
type AgentScope ¶ added in v0.3.0
type AgentScope struct {
Ring, Unit string
// MaxDepth is the effective maximum chain length for this root.
MaxDepth int
// Remote names the agents.remote entries the root's agents may call.
Remote []string
}
AgentScope is what the policy allows delegated agents of one root principal.
type Agents ¶ added in v0.3.0
type Agents struct {
// MaxDepth bounds the delegation chain (default 2, at most 5).
MaxDepth int `yaml:"maxDepth,omitempty" json:"maxDepth,omitempty"`
// MaxTTLSeconds caps a delegated credential (default 900, at most 3600). It
// never outlives its parent either.
MaxTTLSeconds int `yaml:"maxTTLSeconds,omitempty" json:"maxTTLSeconds,omitempty"`
// Experiments enrolls delegated callers in experiments with their root.
// Default false: agent traffic gets control routing and no shadow.
Experiments bool `yaml:"experiments,omitempty" json:"experiments,omitempty"`
// AllowedModels are the gateway model aliases any delegated actor may call;
// empty = every alias the root may call.
AllowedModels []string `yaml:"allowedModels,omitempty" json:"allowedModels,omitempty"`
// DeniedTools are tools a delegated actor's model call may not declare:
// function names (matched ignoring case) and hosted tool identifiers such
// as web_search, code_execution or mcp:<server> (matched as a prefix,
// ignoring case and underscores; see DeniesTool). With any set, the
// gateway refuses an agent call whose tools it cannot read.
DeniedTools []string `yaml:"deniedTools,omitempty" json:"deniedTools,omitempty"`
// Actors are the agents that may act. A name not listed is refused.
Actors []AgentActor `yaml:"actors" json:"actors"`
// Remote is the allowlist of A2A agents members may call.
Remote []RemoteAgent `yaml:"remote,omitempty" json:"remote,omitempty"`
}
Agents is halos.yaml agents: which agents may act for whom, how deep and for how long, what delegated actors may not do, and the remote (A2A) agents members may call.
func (*Agents) Actor ¶ added in v0.3.0
func (a *Agents) Actor(name string) *AgentActor
Actor returns the named agent, or nil.
func (*Agents) AdmitChain ¶ added in v0.3.0
func (a *Agents) AdmitChain(rootGroups []string, d *DelegationChain) error
AdmitChain re-checks a delegation chain against the current policy for a root holding rootGroups: every actor, not only the current one, must still be in agents.actors and, when the actor lists groups, the root must hold one of them; the chain must be within agents.maxDepth. The credential says what policy allowed at issuance, not what it allows now.
func (*Agents) AllowsExperiments ¶ added in v0.3.0
AllowsExperiments reports whether delegated callers may be enrolled in experiments.
func (*Agents) ChainModelLimit ¶ added in v0.3.0
func (a *Agents) ChainModelLimit(d *DelegationChain) []string
ChainModelLimit is a delegation chain's model allowlist under the current policy: the credential's scope narrowed by agents.allowedModels and by the models of every actor along the chain. nil = unrestricted; a non-nil empty slice allows nothing.
func (*Agents) DeniesTool ¶ added in v0.3.0
DeniesTool reports whether agents.deniedTools denies one tool a model call declares. A function tool (run by the client, named in the request) matches an entry ignoring case. A hosted tool (run by the provider or built in, identified by its type, its Gemini or Bedrock tool key, or mcp:<label>) matches an entry when, ignoring case and underscores, its identifier starts with the entry: web_search denies web_search_20250305 and web_search_preview, code_execution denies codeExecution, and mcp denies every mcp:<label>. Hosted identifiers carry versions and vendor spellings, so an exact match would let the next version through.
func (*Agents) ModelLimit ¶ added in v0.3.0
func (a *Agents) ModelLimit(actor *AgentActor) []string
ModelLimit is the policy's model allowlist for actor: agents.allowedModels narrowed by the actor's own models. nil = unrestricted. A non-nil empty slice means nothing is allowed (the two lists do not intersect).
func (*Agents) RemoteAgent ¶ added in v0.3.0
func (a *Agents) RemoteAgent(name string) *RemoteAgent
RemoteAgent returns the named remote agent, or nil.
type AllowFinding ¶ added in v0.4.0
type AllowFinding struct {
Digest string `yaml:"digest" json:"digest"`
Rule string `yaml:"rule" json:"rule"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
Reason string `yaml:"reason" json:"reason"`
}
AllowFinding suppresses scan findings by digest, so changed content is scanned afresh: Digest is either the artifact digest (any of its files) or one file's sha256 (that file only). Path ("" = any file) narrows it.
type Approval ¶ added in v0.4.0
type Approval struct {
By string `json:"by"`
// Groups are the approver's groups when they approved (IdP or SCIM).
Groups []string `json:"groups,omitempty"`
At time.Time `json:"at"`
Ticket string `json:"ticket,omitempty"`
// Scopes are the scopes of the approver's scoped approve bindings when
// they approved (ADR-0037). nil means a global approver: every approval
// recorded before ADR-0037 was global. A scoped approval counts only for
// a change one of them covers (QuorumFor).
Scopes []RoleScope `json:"scopes,omitempty"`
// ScopedGroups replaces Groups on a scoped approval (ScopedApproval): a
// binary from before ADR-0037 ignores Scopes, so it must see no groups
// and count the approval for no rule, rather than count it as global.
ScopedGroups []string `json:"scopedGroups,omitempty"`
}
Approval is one recorded approval of a change.
func ScopedApproval ¶ added in v0.5.0
func ScopedApproval(by string, groups []string, at time.Time, ticket string, scopes []RoleScope) Approval
ScopedApproval is an approval by by, holding groups, under scoped approve bindings with scopes (nil scopes: a global approval).
func (Approval) ScopeCovers ¶ added in v0.5.0
ScopeCovers reports whether a counts for a change touching res: a global approval (Scopes nil) always does, a scoped one when one of its scopes covers res.
type ApprovalRule ¶ added in v0.4.0
type ApprovalRule struct {
Name string `yaml:"name" json:"name"`
Scope ChangeScope `yaml:"scope,omitempty" json:"scope,omitzero"`
Quorum int `yaml:"quorum" json:"quorum"`
// Groups are the IdP (or SCIM) groups an approver must hold one of.
Groups []string `yaml:"groups" json:"groups"`
// DistinctGroups: each counted approval must come from a different group.
DistinctGroups bool `yaml:"distinctGroups,omitempty" json:"distinctGroups,omitempty"`
// Ticket, when set, is a regular expression (matched whole) a change
// ticket reference must match, e.g. ^(CHG|INC)[0-9]{7}$ or ^OPS-[0-9]+$.
Ticket string `yaml:"ticket,omitempty" json:"ticket,omitempty"`
}
ApprovalRule requires Quorum approvals, from members of Groups, for every change its scope covers. The requester never counts.
type Artifact ¶ added in v0.4.0
type Artifact struct {
Name string `yaml:"name" json:"name"`
Kind string `yaml:"kind" json:"kind"`
// Path is a file or directory in the policy repo, relative to its root.
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// Git is a repository pinned to a full commit id.
Git *GitSource `yaml:"git,omitempty" json:"git,omitempty"`
// OCI is a digest-pinned reference, host/repo@sha256:<hex>; each layer is
// one file named by its org.opencontainers.image.title annotation.
OCI string `yaml:"oci,omitempty" json:"oci,omitempty"`
// Digest is the artifact digest (supplychain.DigestOf). Required for git
// and oci; for a path it is computed and, when set, checked.
Digest string `yaml:"digest,omitempty" json:"digest,omitempty"`
Signature *ArtifactSignature `yaml:"signature,omitempty" json:"signature,omitempty"`
}
Artifact is one extension artifact. Exactly one source is set.
type ArtifactContent ¶ added in v0.4.0
type ArtifactContent struct {
Files supplychain.Tree
Err string // why it could not be read
Signer string // trusted identity whose signature verified, if any
}
ArtifactContent is an artifact's resolved content: read from the policy repo by Load, or fetched and signature-checked by `halo release build`.
type ArtifactSignature ¶ added in v0.4.0
type ArtifactSignature struct {
Identity string `yaml:"identity" json:"identity"`
File string `yaml:"file" json:"file"`
}
ArtifactSignature names who signed the artifact digest and where the signature is (a file in the policy repo: base64 ed25519, or cosign's sign-blob output).
type Budget ¶ added in v0.3.0
type Budget struct {
Name string `yaml:"name" json:"name"`
// Scope picks the counter: user (one per verified subject), group (one per
// listed group), ring (one per ring) or org (one shared counter).
Scope string `yaml:"scope" json:"scope"`
// Groups: for scope group, the groups that each get a counter (required);
// for other scopes, only callers in one of them are counted. Empty = everyone.
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
// Rings limits the budget to callers routed to these rings. Empty = every ring.
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
// Window: day | week (Monday) | month, calendar windows in UTC, or rolling
// over Period.
Window string `yaml:"window" json:"window"`
// Period is the rolling window length as a Go duration (e.g. 24h); rolling only.
Period string `yaml:"period,omitempty" json:"period,omitempty"`
// LimitUSD and LimitTokens (input + output + cache) cap the window; set at
// least one. Either one reached exhausts the budget.
LimitUSD float64 `yaml:"limitUSD,omitempty" json:"limitUSD,omitempty"`
LimitTokens int64 `yaml:"limitTokens,omitempty" json:"limitTokens,omitempty"`
// Action when exhausted: warn (header + metric, the request passes) or
// enforce (HTTP 429 until the window resets). Empty means warn.
Action string `yaml:"action,omitempty" json:"action,omitempty"`
// AlertAt are soft thresholds as fractions of the limit (default [0.8]).
AlertAt []float64 `yaml:"alertAt,omitempty" json:"alertAt,omitempty"`
}
Budget caps model spend over a window for one scope (ADR-0017).
func (*Budget) RollingPeriod ¶ added in v0.3.0
RollingPeriod is the parsed Period (0 unless the window is rolling and valid).
type ChangeScope ¶ added in v0.4.0
type ChangeScope struct {
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Units []string `yaml:"units,omitempty" json:"units,omitempty"`
}
ChangeScope selects what a window or rule covers: changes to any listed ring or any listed unit's members. Empty is org-wide.
type ChangeTarget ¶ added in v0.4.0
type ChangeTarget struct {
Rings []string `json:"rings,omitempty"`
Units []string `json:"units,omitempty"`
}
ChangeTarget is what a change touches: rings and units.
type CloudAgent ¶ added in v0.4.0
type CloudAgent struct {
// Enabled false renders nothing that runs the agent and makes every
// observed task a violation; it never deletes a vendor setting.
Enabled bool `yaml:"enabled" json:"enabled"`
// Repos the agent may work in: owner/repo or owner/* (every repo of owner).
Repos []string `yaml:"repos,omitempty" json:"repos,omitempty"`
// Models the agent may use (vendor model ids). The first is rendered as
// the default where the vendor takes a model in config.
Models []string `yaml:"models,omitempty" json:"models,omitempty"`
// MaxConcurrent caps running tasks.
MaxConcurrent CloudConcurrency `yaml:"maxConcurrent,omitempty" json:"maxConcurrent,omitempty"`
// Budgets cap vendor-reported task spend (ADR-0017 budget fields).
Budgets []Budget `yaml:"budgets,omitempty" json:"budgets,omitempty"`
// Network is the egress the agent's sandbox gets, where the vendor has one.
Network *CloudNetwork `yaml:"network,omitempty" json:"network,omitempty"`
// Identity "workload" makes rendered workflows call the model through the
// Halos gateway with a short-lived workload credential (ADR-0023) instead
// of a long-lived vendor key. Empty uses the vendor's repository secret.
Identity string `yaml:"identity,omitempty" json:"identity,omitempty"`
// OnOverLimit is alert (default: report only) or pause (also pause the
// agent where the vendor API can, reversibly; never auto-resumed).
OnOverLimit string `yaml:"onOverLimit,omitempty" json:"onOverLimit,omitempty"`
// Version pins the agent CLI where the vendor takes one (gemini).
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// TimeoutMinutes caps one rendered workflow job (copilot at most 59).
TimeoutMinutes int `yaml:"timeoutMinutes,omitempty" json:"timeoutMinutes,omitempty"`
}
CloudAgent is one vendor's cloud agent policy.
func (*CloudAgent) ModelAllowed ¶ added in v0.4.0
func (a *CloudAgent) ModelAllowed(model string) bool
ModelAllowed reports whether model may be used. No list means any model; an empty model (the vendor did not report one) is allowed.
func (*CloudAgent) Pauses ¶ added in v0.4.0
func (a *CloudAgent) Pauses() bool
Pauses reports whether the agent pauses on an over-limit.
func (*CloudAgent) RepoAllowed ¶ added in v0.4.0
func (a *CloudAgent) RepoAllowed(repo string) bool
RepoAllowed reports whether owner/repo matches one of a's repo patterns (case-insensitive). No patterns means every repository.
type CloudAgents ¶ added in v0.4.0
type CloudAgents struct {
// Identities map vendor logins to Halos users, so a cloud task is
// attributed, ring-resolved and killed like its human.
Identities []CloudIdentity `yaml:"identities,omitempty" json:"identities,omitempty"`
// Server is the halo-server base URL rendered workflows exchange their
// OIDC token at (identity: workload).
Server string `yaml:"server,omitempty" json:"server,omitempty"`
// HaloVersion is the halo release tag rendered workflows install to do
// that exchange (identity: workload).
HaloVersion string `yaml:"haloVersion,omitempty" json:"haloVersion,omitempty"`
Claude *CloudAgent `yaml:"claude,omitempty" json:"claude,omitempty"`
Codex *CloudAgent `yaml:"codex,omitempty" json:"codex,omitempty"`
Copilot *CloudAgent `yaml:"copilot,omitempty" json:"copilot,omitempty"`
Cursor *CloudAgent `yaml:"cursor,omitempty" json:"cursor,omitempty"`
Gemini *CloudAgent `yaml:"gemini,omitempty" json:"gemini,omitempty"`
}
CloudAgents governs background coding agents that run on vendor or CI infrastructure instead of a developer machine (ADR-0034): Claude Code (the GitHub Action and cloud sessions), Codex cloud tasks, the Copilot cloud agent, Cursor cloud agents and the Gemini CLI GitHub Action. A vendor is governed when its key is present. Credentials never live here.
func (*CloudAgents) UserForGitHub ¶ added in v0.4.0
func (c *CloudAgents) UserForGitHub(login string) (string, bool)
UserForGitHub is the Halos user a GitHub login maps to (case-insensitive, as GitHub logins are).
func (*CloudAgents) Vendor ¶ added in v0.4.0
func (c *CloudAgents) Vendor(name string) *CloudAgent
Vendor returns one vendor's policy (nil when ungoverned).
type CloudConcurrency ¶ added in v0.4.0
type CloudConcurrency struct {
PerUser int `yaml:"perUser,omitempty" json:"perUser,omitempty"`
PerGroup int `yaml:"perGroup,omitempty" json:"perGroup,omitempty"`
// Groups that each get a PerGroup counter (required with perGroup).
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
}
CloudConcurrency caps running tasks per user and per group.
type CloudIdentity ¶ added in v0.4.0
type CloudIdentity struct {
// User is the Halos user id (what rings, experiments and budgets key on).
User string `yaml:"user" json:"user"`
// GitHub is the GitHub login that triggers GitHub-hosted agents
// (Claude and Gemini actions, Copilot cloud agent).
GitHub string `yaml:"github,omitempty" json:"github,omitempty"`
}
CloudIdentity is one person's vendor logins.
type CloudNetwork ¶ added in v0.4.0
type CloudNetwork struct {
// Mode: none (no egress), allowlist (only Allow, plus the vendor's
// package-registry list when Recommended) or full (any host; warned).
Mode string `yaml:"mode" json:"mode"`
// Allow are extra domains (a leading "*." matches subdomains).
Allow []string `yaml:"allow,omitempty" json:"allow,omitempty"`
// Recommended keeps the vendor's own dependency allowlist (allowlist only).
Recommended bool `yaml:"recommended,omitempty" json:"recommended,omitempty"`
}
CloudNetwork is the sandbox egress mode.
type Credential ¶
type Credential struct {
Env string `yaml:"env,omitempty" json:"env,omitempty"`
File string `yaml:"file,omitempty" json:"file,omitempty"`
Scheme string `yaml:"scheme,omitempty" json:"scheme,omitempty"`
}
Credential locates a secret on the halo-proxy host (kinds openai, azure-openai, gemini): an environment variable name or a file path (exactly one). Scheme is api-key (default for azure-openai) or bearer (default for openai).
type CredentialRef ¶ added in v0.4.0
type CredentialRef struct {
// IDs is the credential's jti, then its ancestors' (parent first).
IDs []string
// IssuedAt is the credential's iat (unix seconds).
IssuedAt int64
}
CredentialRef identifies a halo-server-signed credential for revocation checks.
type DLP ¶ added in v0.4.0
type DLP struct {
// Secrets: outbound secret detection on request content.
Secrets DLPSecrets `yaml:"secrets,omitempty" json:"secrets,omitzero"`
// Injection: prompt-injection signals on tool results entering the model.
Injection DLPInjection `yaml:"injection,omitempty" json:"injection,omitzero"`
// Rules override the actions per ring and org unit; the first rule whose
// rings and units both match the caller wins.
Rules []DLPRule `yaml:"rules,omitempty" json:"rules,omitempty"`
// MaxScanBytes bounds how much of one request body is scanned (default
// 4 MiB). A larger body is scanned up to the cap and counted as truncated.
MaxScanBytes int64 `yaml:"maxScanBytes,omitempty" json:"maxScanBytes,omitempty"`
// ScanResponses scans streamed and plain responses for echoed secrets
// (monitor only, halo-proxy). Default true.
ScanResponses *bool `yaml:"scanResponses,omitempty" json:"scanResponses,omitempty"`
}
DLP is the gateway's data-loss prevention and prompt-injection defense (ADR-0033). halo-proxy and halo-kong scan model-call request bodies; halo-proxy also scans streamed responses (monitor only).
func (*DLP) Actions ¶ added in v0.4.0
Actions returns the secret and injection actions for a caller in ring and unit chain units (the caller's unit first, then its ancestors): the first matching rule's non-empty actions over the defaults. off disables a scan.
type DLPInjection ¶ added in v0.4.0
type DLPInjection struct {
// Action: monitor (count and audit) or block (refuse at or above
// Threshold). Default monitor. Model content is never modified.
Action string `yaml:"action,omitempty" json:"action,omitempty"`
// Threshold is the score at which a tool result counts as an injection
// (default 4).
Threshold int `yaml:"threshold,omitempty" json:"threshold,omitempty"`
}
DLPInjection configures prompt-injection scoring.
func (*DLPInjection) ScoreThreshold ¶ added in v0.4.0
func (d *DLPInjection) ScoreThreshold() int
ScoreThreshold is Threshold with the default applied.
type DLPPattern ¶ added in v0.4.0
DLPPattern is one org-defined secret detector.
type DLPRule ¶ added in v0.4.0
type DLPRule struct {
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Units []string `yaml:"units,omitempty" json:"units,omitempty"`
Secrets string `yaml:"secrets,omitempty" json:"secrets,omitempty"`
Injection string `yaml:"injection,omitempty" json:"injection,omitempty"`
}
DLPRule overrides the actions for callers in Rings and Units (empty = any). A unit matches its descendants too.
type DLPSecrets ¶ added in v0.4.0
type DLPSecrets struct {
// Action: monitor (count and audit), redact (replace each secret with
// [REDACTED:<detector>:<hash8>]) or block (refuse the request). Default monitor.
Action string `yaml:"action,omitempty" json:"action,omitempty"`
// Disable turns built-in detectors off by id.
Disable []string `yaml:"disable,omitempty" json:"disable,omitempty"`
// Patterns are org detectors (RE2). The whole match is the secret unless
// the regex has a capture group, then the first group is.
Patterns []DLPPattern `yaml:"patterns,omitempty" json:"patterns,omitempty"`
}
DLPSecrets configures the secret detectors.
type Delegation ¶ added in v0.3.0
type Delegation struct {
// AllowHooks lets units add hooks.items. Default false: a hook is a shell
// command every member's harness runs, so a unit owner could otherwise run
// code on any member's machine.
AllowHooks bool `yaml:"allowHooks,omitempty" json:"allowHooks,omitempty"`
// EnvKeys are the env names units may add (exact). Provider, base-URL,
// proxy and runtime-injection names (see UnitForbiddenEnv) are refused
// even when listed.
EnvKeys []string `yaml:"envKeys,omitempty" json:"envKeys,omitempty"`
}
Delegation is what the org lets units add on top of the baseline (halos.yaml delegation:). Anything else a unit adds is a loosening.
type DelegationChain ¶ added in v0.3.0
type DelegationChain struct {
// Actors is the chain: the root's own delegate first, the current actor last.
Actors []Actor
// Models narrows the model aliases the credential may call; nil = not
// narrowed by the credential (policy still applies).
Models []string
// ID is the delegated credential's jti (audit correlation).
ID string
// AuthTime is when the root authenticated (unix seconds, the signed
// auth_time claim): a human root's session revocation after it kills the chain.
AuthTime int64
}
DelegationChain is set on a Subject when an agent acts for it. Subject.ID, Groups and Kind stay the root principal's, so cohort, ring, budgets and posture are the root's.
func (*DelegationChain) Chain ¶ added in v0.3.0
func (d *DelegationChain) Chain() string
Chain renders the actor chain root-side first, e.g. "planner>reviewer".
func (*DelegationChain) Current ¶ added in v0.3.0
func (d *DelegationChain) Current() Actor
Current is the agent making the call.
func (*DelegationChain) Depth ¶ added in v0.3.0
func (d *DelegationChain) Depth() int
Depth is the number of agents in the chain.
type Egress ¶
type Egress struct {
// AllowedDomains is the allowlist: exact hosts or *.domain (subdomains only).
AllowedDomains []string `yaml:"allowedDomains,omitempty" json:"allowedDomains,omitempty"`
// Default is allow | deny for hosts no rule matches. Unset: deny when
// anything is allowed (allowedDomains or presets), allow otherwise.
Default string `yaml:"default,omitempty" json:"default,omitempty"`
// DeniedDomains always lose: a deny rule wins over any allow rule. Unions
// down an extends chain and across units.
DeniedDomains []string `yaml:"deniedDomains,omitempty" json:"deniedDomains,omitempty"`
// Presets are named domain sets added to the allowlist (EgressPresets):
// package-registries | vcs | model-providers.
Presets []string `yaml:"presets,omitempty" json:"presets,omitempty"`
}
Egress is the network egress policy for the agent's shell and fetch tools (ADR-0032). See Egress.Rules for how the fields combine.
func (Egress) Restricted ¶ added in v0.4.0
Restricted reports whether the profile says anything about egress.
func (Egress) Rules ¶ added in v0.4.0
func (e Egress) Rules() EgressRules
Rules resolves e: presets join the allow list after allowedDomains (lower-cased, de-duplicated, first occurrence kept), and an unset default is deny when anything is allowed (allowedDomains has always meant "the only egress permitted") and allow otherwise.
type EgressRules ¶ added in v0.4.0
type EgressRules struct {
Default string `json:"default"` // allow | deny
Allow []string `json:"allow,omitempty"`
Deny []string `json:"deny,omitempty"`
}
EgressRules is an Egress with its default decided and presets expanded: what every adapter renders and what `halo egress test` evaluates.
type Experiment ¶
type Experiment struct {
Meta `yaml:",inline"`
Type ExperimentType `yaml:"type" json:"type"`
Axis Axis `yaml:"axis" json:"axis"`
Status string `yaml:"status,omitempty" json:"status,omitempty"` // draft | running | paused | concluded
// Rings the experiment draws users from.
Rings []string `yaml:"rings" json:"rings"`
Variants []Variant `yaml:"variants" json:"variants"`
// Shadow only: fraction of eligible requests mirrored (0-1).
SampleRate float64 `yaml:"sampleRate,omitempty" json:"sampleRate,omitempty"`
// Shadow only: which turns of a sampled session are mirrored. "first"
// (default) mirrors the opening request; "all" mirrors every step of the
// session, each replayed on the real prefix (ADR-0014).
Turns string `yaml:"turns,omitempty" json:"turns,omitempty"`
Metrics Metrics `yaml:"metrics" json:"metrics"`
Stopping Stopping `yaml:"stopping" json:"stopping"`
// Salt keeps assignment independent across experiments; defaults to Name.
Salt string `yaml:"salt,omitempty" json:"salt,omitempty"`
}
func (*Experiment) Arms ¶
func (e *Experiment) Arms() (control, treatment *Variant, ok bool)
Arms returns the backing experiment's control and treatment variants.
func (*Experiment) AssignVariant ¶ added in v0.4.0
func (e *Experiment) AssignVariant(s Subject, ring string) *Variant
AssignVariant is the variant a subject in ring is (or was) assigned while the experiment runs, whatever its status now; nil when the ring is not enrolled. Analyses of a paused or concluded experiment use it to recover the arms exactly as ResolveVariant served them.
func (*Experiment) ResolveVariant ¶
func (e *Experiment) ResolveVariant(s Subject, ring string) *Variant
ResolveVariant returns the experiment variant for a subject, or nil if the subject's ring is not enrolled or the experiment isn't running.
type ExperimentType ¶
type ExperimentType string
ExperimentType is how an experiment exposes users.
const ( ExperimentAB ExperimentType = "ab" ExperimentCanary ExperimentType = "canary" ExperimentShadow ExperimentType = "shadow" )
type ExtensionSet ¶ added in v0.3.0
type ExtensionSet struct {
// Deny names that must not load. Unions down an extends chain.
Deny []string `yaml:"deny,omitempty" json:"deny,omitempty"`
// PluginOnly allows only extensions that arrive via plugins or managed
// settings (the allowlist is then the allowed marketplaces); user and
// project sources are blocked.
PluginOnly bool `yaml:"pluginOnly,omitempty" json:"pluginOnly,omitempty"`
}
ExtensionSet governs a user-authored extension kind (skills or subagents).
type Extensions ¶ added in v0.3.0
type Extensions struct {
// Marketplaces is the allowlist of plugin marketplace sources; empty = any.
Marketplaces []Marketplace `yaml:"marketplaces,omitempty" json:"marketplaces,omitempty"`
// BlockedMarketplaces are sources users may never use. Unions down an extends chain.
BlockedMarketplaces []Marketplace `yaml:"blockedMarketplaces,omitempty" json:"blockedMarketplaces,omitempty"`
Plugins Plugins `yaml:"plugins,omitempty" json:"plugins,omitempty"`
Skills ExtensionSet `yaml:"skills,omitempty" json:"skills,omitempty"`
Subagents ExtensionSet `yaml:"subagents,omitempty" json:"subagents,omitempty"`
// OutputStyle selects the harness's output style by name.
OutputStyle string `yaml:"outputStyle,omitempty" json:"outputStyle,omitempty"`
// StatusLine runs a managed command to render the status line.
StatusLine StatusLine `yaml:"statusLine,omitempty" json:"statusLine,omitempty"`
// Artifacts names halos.yaml supplyChain.artifacts this profile delivers
// (ADR-0028). Unions down an extends chain.
Artifacts []string `yaml:"artifacts,omitempty" json:"artifacts,omitempty"`
}
Extensions is the plugin/skill/subagent governance surface. Unset means unrestricted; each adapter renders what its harness can enforce and warns about the rest.
type FieldSource ¶ added in v0.3.0
type FieldSource struct {
Path string `json:"path"`
Value string `json:"value"`
Source string `json:"source"`
}
FieldSource is one leaf of an effective profile and the layer that set it.
type FreezeStatus ¶ added in v0.4.0
type FreezeStatus struct {
Frozen bool `json:"frozen"`
Active []ActiveFreeze `json:"active,omitempty"`
// NextOpen is the first instant no window covers the change. Zero while
// an incident freeze holds, or when windows chain for over a year.
NextOpen time.Time `json:"nextOpen,omitzero"`
}
FreezeStatus is whether a change may go out now.
func (*FreezeStatus) AddIncident ¶ added in v0.4.0
func (s *FreezeStatus) AddIncident(by, reason string)
AddIncident adds the incident freeze (halo-server) to s.
func (FreezeStatus) String ¶ added in v0.4.0
func (s FreezeStatus) String() string
String is the refusal message: every active window and the next open time.
type FreezeWindow ¶ added in v0.4.0
type FreezeWindow struct {
Name string `yaml:"name" json:"name"`
Reason string `yaml:"reason" json:"reason"`
Scope ChangeScope `yaml:"scope,omitempty" json:"scope,omitzero"`
// Timezone is an IANA zone (America/New_York); default UTC. Start, End and
// Weekly are wall-clock times there, so DST moves the instant, not the time.
Timezone string `yaml:"timezone,omitempty" json:"timezone,omitempty"`
// Start and End bound a one-off window [start, end): "2026-12-20T18:00",
// "2026-12-20" (midnight) or RFC 3339 with an offset.
Start string `yaml:"start,omitempty" json:"start,omitempty"`
End string `yaml:"end,omitempty" json:"end,omitempty"`
// Weekly repeats every week from From to To.
Weekly *WeeklyWindow `yaml:"weekly,omitempty" json:"weekly,omitempty"`
}
FreezeWindow is one change-freeze window: one-off (start, end) or recurring every week (weekly), in Timezone.
type Gateway ¶
type Gateway struct {
Meta `yaml:",inline"`
// BaseURL is what clients are pointed at (Kong's public listener).
BaseURL string `yaml:"baseURL" json:"baseURL"`
// Protocol per harness: anthropic-messages | bedrock-invoke | openai-responses | gemini.
Protocols map[string]string `yaml:"protocols,omitempty" json:"protocols,omitempty"`
// Auth: how clients obtain a credential for the auth gateway.
Auth GatewayAuth `yaml:"auth" json:"auth"`
// Models maps a stable alias (what clients request) to upstream targets.
Models map[string]ModelRoute `yaml:"models" json:"models"`
// Upstreams are named backends (e.g. the orchestrator, direct Anthropic).
Upstreams map[string]Upstream `yaml:"upstreams" json:"upstreams"`
// Engine is the data plane that enforces this policy: halo-proxy (default),
// kong, or external (a gateway Halos does not run, e.g. a company's own API
// gateway: clients are rendered with each alias's provider model id, since
// nothing translates aliases). Multi-target routes (weights, failover) are
// halo-proxy-only; Kong uses each route's first target.
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
// Pricing is USD per million tokens keyed by provider model id (the model a
// route target names). It costs gateway usage for Budgets.
Pricing map[string]ModelPrice `yaml:"pricing,omitempty" json:"pricing,omitempty"`
// Budgets cap spend per user, group, ring or org (ADR-0017). halo-proxy
// enforces them; halo-server holds the counters.
Budgets []Budget `yaml:"budgets,omitempty" json:"budgets,omitempty"`
// DLP is data-loss prevention and prompt-injection defense on model calls (ADR-0033).
DLP *DLP `yaml:"dlp,omitempty" json:"dlp,omitempty"`
}
Gateway describes how harness traffic reaches models.
func (*Gateway) HarnessWire ¶
HarnessWire is the wire harness h speaks to this gateway ("" = the gateway does not route it).
func (*Gateway) WireFit ¶
func (g *Gateway) WireFit(alias, wire string) (WireFitness, []string)
WireFit reports whether alias routes to an upstream that answers wire, and lists each candidate target as "upstream (kind)". It is the check `halo validate` applies to a harness's start model (guardHarnessWire).
type GatewayAuth ¶
type GatewayAuth struct {
// HelperCommand prints a short-lived token (used as Claude apiKeyHelper, Codex env_key source, ...).
HelperCommand string `yaml:"helperCommand,omitempty" json:"helperCommand,omitempty"`
TTLSeconds int `yaml:"ttlSeconds,omitempty" json:"ttlSeconds,omitempty"`
// IdentityHeader is the header the auth gateway sets with the verified user id.
IdentityHeader string `yaml:"identityHeader,omitempty" json:"identityHeader,omitempty"`
}
type GitSource ¶ added in v0.4.0
type GitSource struct {
URL string `yaml:"url" json:"url"`
Commit string `yaml:"commit" json:"commit"`
// Path within the repository ("" = its root).
Path string `yaml:"path,omitempty" json:"path,omitempty"`
}
GitSource is a git repository at an exact commit.
type Governance ¶ added in v0.4.0
type Governance struct {
// Freezes are change-freeze windows. While one covers a change, the rollout
// controller does not advance, and promotion and advance proposals are
// refused. Rollbacks, pauses and the kill switch are always allowed.
Freezes []FreezeWindow `yaml:"freezes,omitempty" json:"freezes,omitempty"`
// Approvals are approver quorums. Every rule whose scope covers a change
// must be met before it goes out.
Approvals []ApprovalRule `yaml:"approvals,omitempty" json:"approvals,omitempty"`
}
Governance is halos.yaml governance: (ADR-0030): when changes may go out (freeze windows) and who must approve them (approval rules).
type Grant ¶ added in v0.5.0
type Grant struct {
Role string `json:"role"`
Scope RoleScope `json:"scope"`
Source string `json:"source"`
}
Grant is one console role a subject holds over a scope, and the policy field it comes from (for /me, `halo whoami` and audit entries).
type Guardrail ¶
Guardrail is an org-wide policy check. ponytail: guardrails are plain Go funcs, not OPA (ADR-0007). An org-specific evaluator (Rego later) plugs in through ExtraGuardrails; no other code changes.
type HarnessSpec ¶
type HarnessSpec struct {
// Version is an exact CLI version pin (e.g. "2.1.280"). Required for rings.
Version string `yaml:"version" json:"version"`
// Model is this harness's default model alias, replacing models.default
// for it (e.g. codex-default, so codex does not ask for a Claude alias).
// Must be a gateway.models alias, and in models.allowed when enforced.
Model string `yaml:"model,omitempty" json:"model,omitempty"`
// Overrides are raw, harness-native keys merged last. Escape hatch; the Go guardrails still check them (ADR-0007).
Overrides map[string]any `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}
type Identity ¶
type Identity struct {
Issuer string `yaml:"issuer,omitempty" json:"issuer,omitempty"`
ClientID string `yaml:"clientID,omitempty" json:"clientID,omitempty"`
// Audience expected in gateway bearer tokens; defaults to ClientID.
Audience string `yaml:"audience,omitempty" json:"audience,omitempty"`
// UserClaim / GroupsClaim name the JWT claims for subject id and groups.
UserClaim string `yaml:"userClaim,omitempty" json:"userClaim,omitempty"` // default "email"
GroupsClaim string `yaml:"groupsClaim,omitempty" json:"groupsClaim,omitempty"` // default "groups"
// AdminGroups hold halo-server's admin role; same as Roles.Admin (kept for
// existing policy repos).
AdminGroups []string `yaml:"adminGroups,omitempty" json:"adminGroups,omitempty"`
// Roles maps halo-server roles to IdP groups (ADR-0015). omitzero keeps
// the JSON form, and so existing snapshot digests, unchanged when unset.
Roles RoleGroups `yaml:"roles,omitempty" json:"roles,omitzero"`
// RoleBindings grant a role over a scope of rings and units to groups
// and users (ADR-0037); identity.roles and adminGroups stay global.
// Omitted when empty, so existing snapshot digests are unchanged.
RoleBindings []RoleBinding `yaml:"roleBindings,omitempty" json:"roleBindings,omitempty"`
// GroupsOverage is what halo-server's console login does when the ID
// token says the user's groups did not fit in it (Entra ID past 200
// groups: _claim_names / hasgroups): deny (default) refuses the login with
// a clear error; graph reads them from Microsoft Graph with the login's
// access token (ADR-0036). Never a login with no groups.
GroupsOverage string `yaml:"groupsOverage,omitempty" json:"groupsOverage,omitempty"`
// RequireAcr: the console login's ID token acr must be one of these.
// RequireAmr: its amr must contain at least one of these (e.g. [mfa]).
// halo-server console login only; gateways do not check them (ADR-0036).
RequireAcr []string `yaml:"requireAcr,omitempty" json:"requireAcr,omitempty"`
RequireAmr []string `yaml:"requireAmr,omitempty" json:"requireAmr,omitempty"`
}
Identity is the org's OIDC provider. Any compliant IdP works (Okta, Entra ID, Google, Keycloak, Ping, Auth0...). Used by halo-server (portal login), halo-proxy / halo-kong (verifying the caller) and halod enrollment.
func (Identity) Grants ¶ added in v0.5.0
Grants returns every console role s holds under id: the global grants of identity.adminGroups and identity.roles (in ConsoleRoles order), then each identity.roleBindings entry that selects s by group or user, in file order. Exact duplicates (same role and scope) are dropped.
func (Identity) RoleBindingGroups ¶ added in v0.5.0
RoleBindingGroups is every group a role binding selects (halo-server keeps these in the session, like the identity.roles groups).
type ImportOptions ¶ added in v0.4.0
type ImportOptions struct {
ID string // registry id; default derived from the server.json name
Remote bool // pin the remote (streamable HTTP) endpoint, not a package
SHA256 string // image digest, required for an oci package
}
ImportOptions picks what ImportServerJSON pins.
type Issue ¶
Issue is one validation finding. Path is like "rings[ring1].membership.percent".
func SecurityIssues ¶ added in v0.3.0
SecurityIssues returns o's error-severity Validate issues on security fields (securityPaths). halo-server and the gateways run it on every policy load and refuse to activate a policy that has any, keeping the last good one: `halo validate` and `halo release build` are not the only gate.
func TrafficOverlaps ¶
TrafficOverlaps is the guard's finding for gateways, which log it when they load a snapshot that was never validated (they still route first by name).
type Kind ¶
type Kind string
Kind names a policy document type.
const KindRollout Kind = "Rollout"
KindRollout is a phased rollout of one change (see Rollout).
const KindToggle Kind = "Toggle"
KindToggle is a feature toggle document: one capability a platform team can turn on for a targeted cohort, and kill instantly, without a new release.
const KindUnit Kind = "Unit"
KindUnit is a node of the org hierarchy (ADR-0018): a business unit or team with delegated owners, a member selector and an overlay applied on top of the org baseline (the profile a user's ring, or experiment variant, ships).
type MCPAuth ¶ added in v0.4.0
type MCPAuth struct {
// Mode: none (default) | oauth | enterprise | halos.
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
// Issuer is the server's authorization server issuer (oauth, enterprise).
Issuer string `yaml:"issuer,omitempty" json:"issuer,omitempty"`
// ClientID is a pre-registered OAuth client id (oauth: at the server's
// authorization server; enterprise: at the org IdP).
ClientID string `yaml:"clientID,omitempty" json:"clientID,omitempty"`
// ClientIDMetadataURL is the https URL of a Client ID Metadata Document
// the org hosts (oauth): the URL is the client id.
ClientIDMetadataURL string `yaml:"clientIDMetadataURL,omitempty" json:"clientIDMetadataURL,omitempty"`
Scopes []string `yaml:"scopes,omitempty" json:"scopes,omitempty"`
}
MCPAuth is how clients authorize to a remote server.
type MCPFinding ¶ added in v0.4.0
type MCPFinding struct {
Harness string `json:"harness"`
File string `json:"file"`
Server string `json:"server"`
// Kind: unapproved | version-drift.
Kind string `json:"kind"`
// Transport: stdio | http.
Transport string `json:"transport,omitempty"`
// Host of an http server's URL (no userinfo, path or query).
Host string `json:"host,omitempty"`
// DefinitionSHA256 is the hex sha256 of the full user definition (URL, or
// command and arguments), to tell definitions apart without showing them.
DefinitionSHA256 string `json:"definitionSha256,omitempty"`
Detail string `json:"detail"`
Fix string `json:"fix"`
}
MCPFinding is one MCP server halod found in a user config that the release's registry does not approve, or approves at another version. It leaves the device, so it never carries the user's command, arguments or URL beyond its host: any of them can hold a credential.
type MCPPackage ¶ added in v0.4.0
type MCPPackage struct {
// Type: npm | pypi | oci | binary.
Type string `yaml:"type" json:"type"`
// Identifier is the package name (npm, pypi) or image reference without
// tag or digest (oci). Unused for binary.
Identifier string `yaml:"identifier,omitempty" json:"identifier,omitempty"`
// Version is the exact version (npm, pypi; no ranges, no "latest"), or the
// image tag for information (oci).
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// SHA256 is "sha256:<hex>": the image digest (oci, required) or the
// executable's digest (binary, required; halod checks it).
SHA256 string `yaml:"sha256,omitempty" json:"sha256,omitempty"`
// Args are appended to the derived command line.
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
}
MCPPackage pins a stdio server's code.
func (MCPPackage) Command ¶ added in v0.4.0
func (p MCPPackage) Command() []string
Command is the pinned command line a package runs as: npx, uvx or docker with the exact version or digest. nil for binary (its command is explicit).
type MCPPin ¶ added in v0.4.0
type MCPPin struct {
ID string `json:"id"`
URL string `json:"url,omitempty"`
Command []string `json:"command,omitempty"`
Package *MCPPackage `json:"package,omitempty"`
// Units, when set, are the only units (and units below them) that get it;
// empty = every member of the ring.
Units []string `json:"units,omitempty"`
}
MCPPin is one registry server as a ring release manifest carries it for halod's drift check: identity, pin and the units it is limited to.
type MCPRegistry ¶ added in v0.4.0
type MCPRegistry struct {
// Mode: allowlist (default) | warn.
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Servers []RegistryServer `yaml:"servers" json:"servers"`
}
MCPRegistry is halos.yaml mcp.
func (*MCPRegistry) Allowlist ¶ added in v0.4.0
func (r *MCPRegistry) Allowlist() bool
Allowlist reports whether the registry is enforced as an allowlist.
func (*MCPRegistry) MCPPins ¶ added in v0.4.0
func (r *MCPRegistry) MCPPins(ring string) []MCPPin
MCPPins lists the registry servers ring's members (or some of its units' members) get, for the release manifest.
func (*MCPRegistry) Server ¶ added in v0.4.0
func (r *MCPRegistry) Server(id string) *RegistryServer
Server returns the registry server id, or nil.
type MCPServer ¶
type MCPServer struct {
Name string `yaml:"name" json:"name"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// Tools and Auth (ADR-0029) are usually filled from the halos.yaml mcp
// registry; omitzero keeps existing snapshot digests unchanged when unset.
Tools MCPTools `yaml:"tools,omitempty" json:"tools,omitzero"`
Auth MCPAuth `yaml:"auth,omitempty" json:"auth,omitzero"`
}
type MCPTokenDecision ¶ added in v0.4.0
MCPTokenDecision is halo-server's verdict on minting a token for a server.
type MCPTools ¶ added in v0.4.0
type MCPTools struct {
Allow []string `yaml:"allow,omitempty" json:"allow,omitempty"`
Deny []string `yaml:"deny,omitempty" json:"deny,omitempty"`
}
MCPTools narrows a server's tools. Deny wins over Allow; empty Allow = all.
type Marketplace ¶ added in v0.3.0
type Marketplace struct {
// Source: github | git | url | hostPattern
Source string `yaml:"source" json:"source"`
Repo string `yaml:"repo,omitempty" json:"repo,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
HostPattern string `yaml:"hostPattern,omitempty" json:"hostPattern,omitempty"`
}
Marketplace is one plugin marketplace source.
type Membership ¶
type Membership struct {
// Users are explicit user ids always in this ring (checked before Groups).
Users []string `yaml:"users,omitempty" json:"users,omitempty"`
// Groups are IdP groups always in this ring.
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
// Percent of remaining users hashed into this ring (0-100, basis points precision).
Percent float64 `yaml:"percent,omitempty" json:"percent,omitempty"`
// Default marks the catch-all ring (GA).
Default bool `yaml:"default,omitempty" json:"default,omitempty"`
// OptIn lets developers join this ring themselves from the portal
// (recorded as a group membership request; admins may auto-approve).
OptIn bool `yaml:"optIn,omitempty" json:"optIn,omitempty"`
}
type Meta ¶
type Meta struct {
APIVersion string `yaml:"apiVersion" json:"apiVersion"`
Kind Kind `yaml:"kind" json:"kind"`
Name string `yaml:"name" json:"name"`
Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"`
}
Meta is common to every document.
type MetricDef ¶
type MetricDef struct {
Name string
Unit string // unit of the value, e.g. ratio, ms, usd
Description string
Sources map[string]string
// Source lists where values come from. When a metric has both online
// sources, internal/promote prefers the gateway for an experiment that has
// gateway data (exact cohort attribution), else the CLI events.
Source []SignalSource
// PerUnit defines one analysis sample: experiments compare the per-unit
// values of the two arms. CLI unit = user.id (Codex: user.account_id, else
// conversation.id; Gemini: installation.id); gateway unit = salted hash
// of the session id (else of the verified subject).
PerUnit string
// Better is the direction of improvement ("increase" | "decrease"); when
// set, goals on this metric must use it. Empty = not enforced.
Better string
}
MetricDef is a normalised halo.* metric. The telemetry collector maps each harness's native signal (Sources, keyed by adapter name) onto Name.
func LookupMetric ¶
LookupMetric returns the registry entry for name.
func (MetricDef) HasSource ¶
func (m MetricDef) HasSource(s SignalSource) bool
HasSource reports whether the metric can be measured by s.
func (MetricDef) ShadowOnly ¶
ShadowOnly reports whether the metric is only measured on shadow pairs.
type MetricGoal ¶
type MetricGoal struct {
// Metric is a normalised halo.* metric name, e.g. halo.task.success, halo.cost.usd_per_session.
Metric string `yaml:"metric" json:"metric"`
// Direction: increase | decrease
Direction string `yaml:"direction" json:"direction"`
// MaxRegression (guardrails): relative worsening that aborts, e.g. 0.05 = 5%.
MaxRegression float64 `yaml:"maxRegression,omitempty" json:"maxRegression,omitempty"`
}
type Metrics ¶
type Metrics struct {
Primary MetricGoal `yaml:"primary" json:"primary"`
Guardrails []MetricGoal `yaml:"guardrails,omitempty" json:"guardrails,omitempty"`
}
type ModelChoice ¶
type ModelChoice []string
ModelChoice is one model id, or several in failover order.
func (*ModelChoice) UnmarshalYAML ¶
func (m *ModelChoice) UnmarshalYAML(n *yaml.Node) error
UnmarshalYAML accepts a scalar or a list of scalars.
type ModelPrice ¶ added in v0.3.0
type ModelPrice struct {
InputPerMTok float64 `yaml:"inputPerMTok" json:"inputPerMTok"`
OutputPerMTok float64 `yaml:"outputPerMTok" json:"outputPerMTok"`
CacheReadPerMTok float64 `yaml:"cacheReadPerMTok,omitempty" json:"cacheReadPerMTok,omitempty"`
CacheWritePerMTok float64 `yaml:"cacheWritePerMTok,omitempty" json:"cacheWritePerMTok,omitempty"`
}
ModelPrice is what one provider model costs, in USD per million tokens. Cache prices of 0 fall back to InputPerMTok: over-counting a cache hit is the safe direction for a budget.
type ModelRoute ¶
type ModelRoute struct {
Upstream string `yaml:"upstream,omitempty" json:"upstream,omitempty"`
// Model is the provider model id / Bedrock inference profile ARN.
Model string `yaml:"model,omitempty" json:"model,omitempty"`
Targets []RouteTarget `yaml:"targets,omitempty" json:"targets,omitempty"`
}
ModelRoute is one alias's routing: either a single upstream+model, or an ordered/weighted list of Targets (mutually exclusive).
func (ModelRoute) Candidates ¶
func (r ModelRoute) Candidates() []RouteTarget
Candidates returns the route's targets (a single-target route is one).
func (ModelRoute) Primary ¶
func (r ModelRoute) Primary() ModelRoute
Primary is the route reduced to its first-priority target, for consumers that cannot fail over (halo-kong, halo-shadow).
type Org ¶
type Org struct {
Name string
Identity Identity
SelfService SelfService
Gateway *Gateway
Profiles map[string]*Profile
Rings []*Ring // ordered, earliest ring first
Experiments []*Experiment
Toggles []*Toggle `json:",omitempty"` // omitted when empty: existing snapshots keep their digest
// Rollouts are omitted when empty, like Toggles.
Rollouts []*Rollout `json:",omitempty"`
// Units are the org hierarchy (ADR-0018), omitted when empty like Toggles.
Units []*Unit `json:",omitempty"`
// Delegation is what units may add beyond the baseline (ADR-0018); omitted
// when unset, like Toggles.
Delegation *Delegation `json:",omitempty"`
// Sources records which repo-relative file declared each document, so
// Validate can keep unit directories (delegated by CODEOWNERS) to Unit
// documents. Not part of the snapshot.
Sources []Source `json:"-"`
// Workloads federates CI / cloud workload identities (ADR-0023); omitted
// when unset, like Toggles.
Workloads *Workloads `json:",omitempty"`
// Agents governs delegated and remote agents (ADR-0024); omitted when
// unset, like Toggles.
Agents *Agents `json:",omitempty"`
// VendorAdmin drives the vendors' cloud admin consoles (ADR-0025); omitted
// when unset, like Toggles.
VendorAdmin *VendorAdmin `json:",omitempty"`
// CloudAgents governs vendor-hosted background agents (ADR-0034); omitted
// when unset, like Toggles.
CloudAgents *CloudAgents `json:",omitempty"`
// Governance holds change-freeze windows and approval quorums (ADR-0030);
// omitted when unset, like Toggles.
Governance *Governance `json:",omitempty"`
// SupplyChain declares the extension artifacts releases deliver
// (ADR-0028); omitted when unset, like Toggles.
SupplyChain *SupplyChain `json:",omitempty"`
// ArtifactContent is each artifact's resolved content, keyed by name:
// path sources read by Load, remote sources fetched by the release
// build. Not part of the snapshot.
ArtifactContent map[string]*ArtifactContent `json:"-"`
// MCP is the approved MCP server registry (ADR-0029); omitted when unset,
// like Toggles.
MCP *MCPRegistry `json:",omitempty"`
// Deprecations are load-time warnings (one per file still on a deprecated
// apiVersion) that Validate reports. Not part of the snapshot.
Deprecations []Issue `json:"-"`
}
Org is the fully loaded policy repo.
func Load ¶
Load reads every *.yaml/*.yml under dir (multi-document files supported), dispatches by kind, and returns the assembled Org. Decoding is strict: unknown fields are errors, reported with the yaml line ("line N") and file.
func ParseSnapshot ¶
ParseSnapshot reads a Compile envelope (verifying version and digest) or, for backwards compatibility, a bare json.Marshal(*Org) document.
func TightenWorkloads ¶ added in v0.3.0
TightenWorkloads is the part of a refused policy next (SecurityIssues found errors) that still applies over good, the last policy that passed: workload rule removals, so an unrelated error elsewhere cannot block the emergency removal of a compromised rule. The result is good's rules that next still has unchanged (equal, under an issuer of the same name), and each dropped rule left in its place as a deny tombstone: it still matches, first-match-wins, but grants nothing. So removing a narrow rule never lets its tokens fall through to a broader rule below it, and every token's grant is a subset of its last-good grant. Additions and modifications are not applied, nor is anything else in next. It returns good itself when no rule is dropped, else a shallow copy of good with its own Workloads, plus the names of the dropped rules. Neither input is modified.
func (*Org) AgentScopeFor ¶ added in v0.3.0
func (o *Org) AgentScopeFor(s Subject) (AgentScope, error)
AgentScopeFor resolves s's ring, profile and unit and returns the effective agent limits. An error means the effective profile could not be computed (a policy that Validate rejects); callers must fail closed.
func (*Org) AgentScopeIn ¶ added in v0.3.0
func (o *Org) AgentScopeIn(ring, unit string) (AgentScope, error)
AgentScopeIn is AgentScopeFor for a caller already resolved to ring and unit ("" = none). It depends only on the policy and the two names, so a caller may memoise it per policy snapshot.
func (*Org) ApprovalRules ¶ added in v0.4.0
func (o *Org) ApprovalRules(target ChangeTarget) []ApprovalRule
ApprovalRules returns the rules covering target.
func (*Org) CheckTicket ¶ added in v0.4.0
func (o *Org) CheckTicket(target ChangeTarget, ticket string) error
CheckTicket validates a change's ticket against every rule covering target that requires one.
func (*Org) ClientExperiments ¶
func (o *Org) ClientExperiments(ring string) []*Experiment
ClientExperiments returns the running client-axis experiments that enroll ring, in policy order. Validate allows at most one per ring.
func (*Org) CodeOwners ¶ added in v0.3.0
CodeOwners renders a CODEOWNERS file delegating each unit's directory to its owners, then its ancestors' owners (any one may approve), after a catch-all line for defaults (the central team, which keeps halos.yaml, profiles and rings). A top-level unit's own file goes to defaults alone (required then): its members draw from the whole org. prefix is the policy directory relative to the repository root ("" or "." when they are the same). Lines are sorted by path, so a nested unit's line, and a unit's file line, follow and override the directory line above them.
func (*Org) DecideMCPToken ¶ added in v0.4.0
func (o *Org) DecideMCPToken(s Subject, resource string) MCPTokenDecision
DecideMCPToken decides whether s may get a Halos access token for the registry server at resource: the server must use auth mode halos and be rendered for s's ring or unit.
func (*Org) DecideRemote ¶ added in v0.3.0
func (o *Org) DecideRemote(s Subject, d *DelegationChain, name, target, skill string) RemoteDecision
DecideRemote decides whether s (with delegation d, may be nil) may call remote agent name (or, when name is empty, the agent whose AgentCard is served from target's origin) and, when skill is set, invoke that skill.
func (*Org) EffectiveProfile ¶ added in v0.3.0
EffectiveProfile returns base with the overlays of unit's chain applied, root first. base is not modified; unit "" returns a copy of base.
func (*Org) ExplainEffective ¶ added in v0.3.0
func (o *Org) ExplainEffective(profile, unit string) (*Profile, []FieldSource, error)
ExplainEffective returns profile's resolved config with unit's overlays applied (unit may be empty) and, for every leaf, the layer that set it: "profile <name>" along the extends chain (root first), then "unit <name>" along the unit chain. A value no single layer holds (appended instructions) names every layer that contributed, joined with " + ".
func (*Org) FindArtifact ¶ added in v0.4.0
FindArtifact returns the artifact declared under name.
func (*Org) FreezeAt ¶ added in v0.4.0
func (o *Org) FreezeAt(t time.Time, target ChangeTarget) FreezeStatus
FreezeAt reports the policy windows covering target at t. The incident freeze lives on halo-server: callers add it (AddIncident).
func (*Org) Loosenings ¶ added in v0.3.0
Loosenings lists the enforced fields child weakens relative to parent. The enforced fields are the org's security baseline: permissions (mode, sandbox, sandboxRequired, disableBypass, deny, allow, ask), mcp.denied and mcp.managedOnly, hooks.managedOnly, telemetry (enabled, endpoint, protocol, logPrompts, existing attributes), egress (allowlist, default, deny list), sandbox (writable roots, protected paths), existing env keys, models.allowed while the parent enforces it, and agents (maxDepth may only go down, remote may only narrow; ADR-0024). Additions that run code or reroute traffic on members' machines are loosenings too, unless o allows them: hooks (delegation.allowHooks), env keys (delegation.envKeys; provider, proxy and runtime-injection keys never), MCP servers (an exact selfService.catalog entry), and any extensions change.
func (*Org) ProfileReach ¶ added in v0.5.0
ProfileReach is every ring an edit to profile name reaches: each ring whose profile is name or extends it (at any depth), and every ring of each experiment with a variant whose profile does. A chain that does not resolve counts as reaching (fail closed). Ring order follows o.Rings.
func (*Org) Quorum ¶ added in v0.4.0
func (o *Org) Quorum(target ChangeTarget, requester, ticket string, approvals []Approval) *QuorumResult
Quorum evaluates approvals of a change requested by requester against every rule covering target; nil when none does. The requester's own approval and repeat approvals never count, and an approver counts for a rule only while holding one of its groups. ticket is the change's ticket reference ("" = none). Scoped approvals never count here: use QuorumFor with what the change touches.
func (*Org) QuorumFor ¶ added in v0.5.0
func (o *Org) QuorumFor(res *Resource, target ChangeTarget, requester, ticket string, approvals []Approval) *QuorumResult
QuorumFor is Quorum for a change that touches res (ADR-0037): an approval made under scoped approve bindings counts only when one of its scopes covers res, and never when res is nil (unknown reach, fail closed).
func (*Org) ResolveProfile ¶
ResolveProfile returns the named profile with its extends chain applied (root ancestor first, child last). Merge semantics:
- scalars: child wins when set (non-zero). ponytail: zero means "unset", so a child cannot turn a parent's true bool back to false; add pointer bools if needed.
- slices: child replaces the parent's list when non-empty, EXCEPT permissions.deny, mcp.denied, hooks.items, extensions.artifacts, the extensions deny/blocked lists, egress.deniedDomains and sandbox.protectedPaths, which union (see below).
- sandbox.mode and permissions.sandbox are one setting: the result carries the effective mode in both.
- maps: merged per key; struct values (e.g. harnesses) merge recursively, other values (e.g. overrides) are child-wins per key.
The result is a deep-ish copy: mutating it does not affect the Org. Extends cycles and missing parents are errors.
func (*Org) ResolveRing ¶
ResolveRing returns the ring a subject belongs to. Rings are walked in order: explicit user ids win, then explicit group membership, then each ring claims its Percent of the hash space (cumulatively, so ring1 5% + ring2 25% = buckets [0,500) and [500,3000)), then the Default ring catches everyone else.
func (*Org) ResolveUnit ¶ added in v0.3.0
ResolveUnit returns the unit s belongs to, or nil (see MatchUnit).
func (*Org) RingTarget ¶ added in v0.4.0
func (o *Org) RingTarget(rings ...string) ChangeTarget
RingTarget is a change to rings. A ring's members can come from any unit, so it touches every unit: a unit-scoped window or rule covers ring changes.
func (*Org) ScopeWithin ¶ added in v0.5.0
ScopeWithin reports whether every resource inner covers, outer covers too: inner's rings are outer's (or outer is ring-unbounded), and each of inner's units is one of outer's or a descendant of one. A service token's scope must be within its creator's (ADR-0037). An unknown unit is never within anything bounded.
func (*Org) SubjectResource ¶ added in v0.5.0
SubjectResource is the resource a change for one member is: their ring and unit chain under o (nil Rings/Units when none resolves).
func (*Org) SubjectTarget ¶ added in v0.4.0
func (o *Org) SubjectTarget(s Subject) ChangeTarget
SubjectTarget is a change for one subject (an access request): their ring and every unit of their unit chain.
func (*Org) UnitChain ¶ added in v0.3.0
UnitChain returns name's ancestry, root first and name last. Unknown units and parent cycles are errors.
func (*Org) Validate ¶
Validate runs semantic checks over the whole Org, then the built-in guardrails and any ExtraGuardrails. It never returns nil-vs-empty ambiguity: no issues = empty slice.
func (*Org) VendorAdminRing ¶ added in v0.3.0
VendorAdminRing is the ring whose profile the consoles get: VendorAdmin.Ring, else the ring with the highest order (GA). Empty when there are no rings.
func (*Org) WithMCPRegistry ¶ added in v0.4.0
WithMCPRegistry returns a copy of p with the registry servers admitted for ring and unit ("" = none) added to mcp.servers, and, in allowlist mode, mcp.managedOnly set. A server p already lists by name is kept as is (a ring release's profile already holds the ring's servers when its units are rendered). p is not modified.
type Overlay ¶ added in v0.3.0
type Overlay struct {
Models Models `yaml:"models,omitempty" json:"models,omitempty"`
Permissions Permissions `yaml:"permissions,omitempty" json:"permissions,omitempty"`
MCP MCP `yaml:"mcp,omitempty" json:"mcp,omitempty"`
Hooks Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty"`
Telemetry Telemetry `yaml:"telemetry,omitempty" json:"telemetry,omitempty"`
Egress Egress `yaml:"egress,omitempty" json:"egress,omitempty"`
Sandbox Sandbox `yaml:"sandbox,omitempty" json:"sandbox,omitzero"`
Instructions string `yaml:"instructions,omitempty" json:"instructions,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
// Agents may only lower maxDepth and narrow remote (ADR-0024).
Agents AgentLimits `yaml:"agents,omitempty" json:"agents,omitzero"`
}
Overlay is the part of a Profile a unit may change. Harness pins and overrides stay central (they ship per ring). Merge onto the baseline: scalars win when set; permissions.deny, mcp.denied and hooks.items union; egress.deniedDomains and sandbox.protectedPaths union; mcp.servers are added (a name already provided above is an error); models.allowed, permissions.allow/ask, egress.allowedDomains/presets and sandbox.writableRoots replace; env and telemetry.attributes merge per key; instructions are appended.
type Permissions ¶
type Permissions struct {
// Mode: default | acceptEdits | plan | auto
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Allow []string `yaml:"allow,omitempty" json:"allow,omitempty"`
Deny []string `yaml:"deny,omitempty" json:"deny,omitempty"`
Ask []string `yaml:"ask,omitempty" json:"ask,omitempty"`
DisableBypass bool `yaml:"disableBypass,omitempty" json:"disableBypass,omitempty"`
// Sandbox: harness sandbox policy — off | workspace-write | read-only
Sandbox string `yaml:"sandbox,omitempty" json:"sandbox,omitempty"`
// SandboxRequired fails closed: the harness must refuse to run (Claude:
// sandbox.failIfUnavailable) rather than fall back to unsandboxed
// execution when its sandbox dependencies are missing.
SandboxRequired bool `yaml:"sandboxRequired,omitempty" json:"sandboxRequired,omitempty"`
}
func SafetyPreset ¶
func SafetyPreset(name string) (Permissions, bool, error)
SafetyPreset returns the permission, MCP and hook settings of preset name ("" = standard). No preset ever sets bypassPermissions, and all set disableBypass.
type Plugins ¶ added in v0.3.0
type Plugins struct {
// Enabled plugins are required on.
Enabled []string `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// Blocked plugins are required off. Unions down an extends chain.
Blocked []string `yaml:"blocked,omitempty" json:"blocked,omitempty"`
}
Plugins pins individual plugins, keyed PLUGIN@MARKETPLACE.
type Profile ¶
type Profile struct {
Meta `yaml:",inline"`
Extends string `yaml:"extends,omitempty" json:"extends,omitempty"`
// Harnesses enabled by this profile, keyed by adapter name.
Harnesses map[string]HarnessSpec `yaml:"harnesses" json:"harnesses"`
Models Models `yaml:"models" json:"models"`
Permissions Permissions `yaml:"permissions" json:"permissions"`
MCP MCP `yaml:"mcp" json:"mcp"`
Hooks Hooks `yaml:"hooks" json:"hooks"`
Telemetry Telemetry `yaml:"telemetry" json:"telemetry"`
Egress Egress `yaml:"egress" json:"egress"`
// Sandbox is the agent sandbox: mode, writable roots, protected paths (ADR-0032).
Sandbox Sandbox `yaml:"sandbox,omitempty" json:"sandbox,omitzero"`
// Extensions governs agent-extension surfaces: plugins and their marketplaces,
// skills, subagents, output style and status line.
Extensions Extensions `yaml:"extensions,omitempty" json:"extensions,omitempty"`
// Agents lowers the delegation depth and narrows the remote agents for
// this profile's members (ADR-0024). Gateway and halo-server enforced.
Agents AgentLimits `yaml:"agents,omitempty" json:"agents,omitzero"`
// Login restricts how users authenticate: subscription (claude.ai / ChatGPT /
// Google login) | api (API key or gateway credential). Empty = unrestricted.
Login string `yaml:"login,omitempty" json:"login,omitempty"`
// Instructions is org-wide memory (managed CLAUDE.md / AGENTS.md / GEMINI.md).
Instructions string `yaml:"instructions,omitempty" json:"instructions,omitempty"`
// Env is extra environment applied to every harness.
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
// Resolved is filled by Org.ResolveProfile from Extensions.Artifacts:
// the artifacts this profile delivers, with content (ADR-0028).
Resolved []ResolvedArtifact `yaml:"-" json:"-"`
}
Profile is desired harness behaviour, independent of any one CLI.
type Provider ¶
type Provider struct {
Name string `yaml:"name" json:"name"`
Region string `yaml:"region,omitempty" json:"region,omitempty"`
Project string `yaml:"project,omitempty" json:"project,omitempty"`
URL string `yaml:"url,omitempty" json:"url,omitempty"`
}
Provider names a model provider and its settings.
type QuorumResult ¶ added in v0.4.0
type QuorumResult struct {
Rules []RuleResult `json:"rules"`
Met bool `json:"met"`
// Ticket is the change's ticket reference ("" = none).
Ticket string `json:"ticket,omitempty"`
// Ignored explains approvals that did not count.
Ignored []string `json:"ignored,omitempty"`
}
QuorumResult is every covering rule's state; Met when all are.
func (*QuorumResult) String ¶ added in v0.4.0
func (q *QuorumResult) String() string
String summarises what is missing, for refusals and gate details.
type RegistryServer ¶ added in v0.4.0
type RegistryServer struct {
// ID is the server name rendered into every harness (and the <server> of
// its tools' names, e.g. mcp__<id>__<tool>): lowercase letters, digits, '-'.
ID string `yaml:"id" json:"id"`
// RegistryName and Version are the official MCP Registry's server.json name
// and version this entry was imported from (`halo mcp registry import`).
RegistryName string `yaml:"registryName,omitempty" json:"registryName,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// Exactly one transport: URL (remote, https), Package (stdio; npm, pypi and
// oci derive the command line) or Command (stdio; required with a binary package).
URL string `yaml:"url,omitempty" json:"url,omitempty"`
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
Package *MCPPackage `yaml:"package,omitempty" json:"package,omitempty"`
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
Tools MCPTools `yaml:"tools,omitempty" json:"tools,omitzero"`
Auth MCPAuth `yaml:"auth,omitempty" json:"auth,omitzero"`
// Rings and Units it is rendered for: a member of a listed ring, or of a
// listed unit or one below it. Both empty = rendered for nobody (approved
// only: a toggle naming it may deliver it).
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Units []string `yaml:"units,omitempty" json:"units,omitempty"`
}
RegistryServer is one approved MCP server.
func ImportServerJSON ¶ added in v0.4.0
func ImportServerJSON(b []byte, o ImportOptions) (s RegistryServer, notes []string, err error)
ImportServerJSON turns a server.json (or a registry API response wrapping one in "server") into a pinned registry entry: the remote URL, or the first npm, pypi or oci stdio package at its exact version. notes lists what the entry does not carry (arguments, environment, headers) and still needs a human: rings or units, auth, tools.
func (RegistryServer) Resolved ¶ added in v0.4.0
func (s RegistryServer) Resolved() MCPServer
Resolved is the server as harness adapters render it: the package's derived command line when it has no explicit one.
type RemoteAgent ¶ added in v0.3.0
type RemoteAgent struct {
Name string `yaml:"name" json:"name"`
// CardURL is the AgentCard, usually https://host/.well-known/agent-card.json.
CardURL string `yaml:"cardURL" json:"cardURL"`
// Digest pins the card: "sha256:<hex>" over its RFC 8785 canonical JSON, as
// `halo agents verify` prints it. A changed card is a policy change.
Digest string `yaml:"digest" json:"digest"`
// PublicKey (PEM) or JWKSURL verifies the card's JWS signatures. With
// either set, an unsigned card or a bad signature fails verification.
PublicKey string `yaml:"publicKey,omitempty" json:"publicKey,omitempty"`
JWKSURL string `yaml:"jwksURL,omitempty" json:"jwksURL,omitempty"`
// Skills are the card's skill ids members may invoke; empty = any.
Skills []string `yaml:"skills,omitempty" json:"skills,omitempty"`
// Rings and Units that may call it: a caller in a listed ring, or in a
// listed unit or one below it, may. Both empty = nobody.
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Units []string `yaml:"units,omitempty" json:"units,omitempty"`
}
RemoteAgent is one allowlisted A2A agent (https://a2a-protocol.org).
type RemoteDecision ¶ added in v0.3.0
type RemoteDecision struct {
Allow bool `json:"allow"`
Agent string `json:"agent,omitempty"`
Digest string `json:"digest,omitempty"`
Reason string `json:"reason"`
Ring string `json:"ring,omitempty"`
Unit string `json:"unit,omitempty"`
}
RemoteDecision is the egress verdict for one call to a remote agent.
type ResolvedArtifact ¶ added in v0.4.0
type ResolvedArtifact struct {
Name, Kind, Source, Digest, Signer string
Files supplychain.Tree
}
ResolvedArtifact is an artifact a resolved profile delivers. Files is nil when the content was not resolved (a remote source outside a release build).
type Resource ¶ added in v0.5.0
type Resource struct {
// Rings it affects; AllRings: members of every ring, and of none (an
// org-wide toggle, a profile shared by several rings, a change whose
// reach is unknown).
Rings []string `json:"rings,omitempty"`
AllRings bool `json:"allRings,omitempty"`
// Units is the unit chain (root first) of the one member it concerns
// (a device, an access request). AllUnits: members of every unit and of
// none: rings, experiments, rollouts and toggles draw from the whole org
// (ADR-0018), so no unit owns them.
Units []string `json:"units,omitempty"`
AllUnits bool `json:"allUnits,omitempty"`
}
Resource is what an authorization decision is about: the rings and units whose members it affects. halo-server derives it from current policy and its own records, never from the client (a ring in a request body or a device's self-reported ring is not trusted).
func RingResource ¶ added in v0.5.0
RingResource is a change to rings: every unit's members, as RingTarget.
type Ring ¶
type Ring struct {
Meta `yaml:",inline"`
Order int `yaml:"order" json:"order"`
Profile string `yaml:"profile" json:"profile"`
// Release pins the ring to an immutable published release digest; empty = build from Profile.
Release string `yaml:"release,omitempty" json:"release,omitempty"`
Membership Membership `yaml:"membership" json:"membership"`
// Posture gates gateway access on the device posture halod reports to
// halo-server (fresh, drift-free, on this ring's release): off | warn | enforce.
// Empty means warn. Takes effect only on gateways wired to halo-server.
Posture string `yaml:"posture,omitempty" json:"posture,omitempty"`
// VersionGate checks the CLI version in the User-Agent against the ring's
// pinned harness version: off | warn | enforce. Empty means warn.
VersionGate string `yaml:"versionGate,omitempty" json:"versionGate,omitempty"`
}
Ring is an ordered rollout cohort pointing at a profile.
type RoleBinding ¶ added in v0.5.0
type RoleBinding struct {
Role string `yaml:"role" json:"role"`
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
Users []string `yaml:"users,omitempty" json:"users,omitempty"`
Scope RoleScope `yaml:"scope,omitempty" json:"scope,omitzero"`
}
RoleBinding grants Role over Scope to every listed group and user (identity.roleBindings). An empty scope is global: the binding means exactly what the same group under identity.roles means.
type RoleGroups ¶ added in v0.3.0
type RoleGroups struct {
Viewer []string `yaml:"viewer,omitempty" json:"viewer,omitempty"` // read fleet, releases, rollouts, experiments, toggles, policy
Operator []string `yaml:"operator,omitempty" json:"operator,omitempty"` // viewer + kill switch, experiment status
Approver []string `yaml:"approver,omitempty" json:"approver,omitempty"` // viewer + decide access requests
Auditor []string `yaml:"auditor,omitempty" json:"auditor,omitempty"` // audit log only
Admin []string `yaml:"admin,omitempty" json:"admin,omitempty"` // everything, incl. devices, sessions, toggle config
}
RoleGroups lists, per halo-server role, the IdP groups that hold it. A user holds every role any of their groups maps to. Strict YAML decoding rejects an unknown role name, so a typo cannot silently grant nothing.
type RoleScope ¶ added in v0.5.0
type RoleScope struct {
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Units []string `yaml:"units,omitempty" json:"units,omitempty"`
}
RoleScope bounds a binding: the rings and the units (each with its descendants) whose resources it covers. Within a list any value matches; across the two lists both must, so {rings: [canary], units: [payments]} is payments' resources in canary. Empty is global.
Deliberately not ChangeScope: an approval rule's scope selects changes it covers (any listed ring OR unit, ADR-0030); a role scope bounds authority, so it is the narrower conjunction and a resource must fall entirely inside it.
func (RoleScope) Covers ¶ added in v0.5.0
Covers reports whether r lies entirely inside s. A bounded dimension never covers a resource that is unbounded, or unknown, in it: deny by default when the scope of a resource cannot be determined.
type Rollout ¶
type Rollout struct {
Meta `yaml:",inline"`
Axis Axis `yaml:"axis" json:"axis"`
// Status: draft | active | paused | aborted | completed ("" = draft).
Status string `yaml:"status,omitempty" json:"status,omitempty"`
// Step is the live step's name, set by merged advance PRs ("" = not started).
Step string `yaml:"step,omitempty" json:"step,omitempty"`
// Experiment is the backing two-arm experiment: exposure, evidence, kill
// switch. Optional when every step is progressive or blue-green.
Experiment string `yaml:"experiment,omitempty" json:"experiment,omitempty"`
Change RolloutChange `yaml:"change" json:"change"`
Baseline RolloutBaseline `yaml:"baseline,omitempty" json:"baseline,omitempty"`
Steps []RolloutStep `yaml:"steps" json:"steps"`
}
Rollout describes how ONE change rolls out: ordered steps, each with a target ring and percent, a minimum bake, gates, and a failure action.
Exposure inside a ring is carried by the backing Experiment (its control and treatment weights, or its shadow sample rate), so assignment stays in internal/assign and the existing kill switch is the traffic-axis rollback. Ring-wide steps move Ring.release. Every edit is proposed as a PR.
func (*Rollout) EffectiveStatus ¶
EffectiveStatus is Status with the draft default.
type RolloutBaseline ¶
type RolloutBaseline struct {
// Release is the client-axis switch-back target (required with ring-wide steps).
Release string `yaml:"release,omitempty" json:"release,omitempty"`
}
RolloutBaseline is the known-good state a rollback returns to.
type RolloutChange ¶
type RolloutChange struct {
// Client axis: the release digest ring pointers move to, and its label.
Release string `yaml:"release,omitempty" json:"release,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// Traffic axis: the gateway model alias whose route becomes the treatment
// variant's route for it when the rollout completes.
Alias string `yaml:"alias,omitempty" json:"alias,omitempty"`
}
RolloutChange is what rolls out.
type RolloutGates ¶
type RolloutGates struct {
// Guardrails compare the backing experiment's arms (internal/stats
// sequential test). A breach triggers OnFailure at any time.
Guardrails []MetricGoal `yaml:"guardrails,omitempty" json:"guardrails,omitempty"`
// Scorecard reads a `halo eval run --output json` scorecard.
Scorecard *ScorecardGate `yaml:"scorecard,omitempty" json:"scorecard,omitempty"`
// Approval holds the step until a human advances it (`halo rollout advance`).
Approval bool `yaml:"approval,omitempty" json:"approval,omitempty"`
}
RolloutGates must all pass before a step advances.
type RolloutPreset ¶
type RolloutPreset struct {
Name string
Rings []Ring
// Canary is the treatment ramp on the backing experiment, in order.
Canary []RolloutStep
// TeamBake is the bake on ring0; RingBake on each later ring before GA.
TeamBake, RingBake string
}
RolloutPreset is a named ring layout plus the step template intent commands stamp into Rollouts. Rings carry no team members (Expand fills ring0).
func LookupRolloutPreset ¶
func LookupRolloutPreset(name string) (RolloutPreset, error)
LookupRolloutPreset returns preset name ("" = standard).
type RolloutStep ¶
type RolloutStep struct {
Name string `yaml:"name" json:"name"`
Strategy RolloutStrategy `yaml:"strategy" json:"strategy"`
// Ring is required for progressive/blue-green. Percent steps expose the
// backing experiment's rings; Ring, if set, must be one of them.
Ring string `yaml:"ring,omitempty" json:"ring,omitempty"`
// Percent: canary = treatment share; holdout = share kept on control;
// dark-launch = share of requests mirrored; progressive/blue-green = 100.
Percent float64 `yaml:"percent,omitempty" json:"percent,omitempty"`
// Bake is the minimum time at this step: Go duration or N"d" (e.g. "14d").
Bake string `yaml:"bake,omitempty" json:"bake,omitempty"`
MinSamples int `yaml:"minSamples,omitempty" json:"minSamples,omitempty"`
Gates RolloutGates `yaml:"gates,omitempty" json:"gates,omitempty"`
// OnFailure: rollback | pause (default rollback).
OnFailure string `yaml:"onFailure,omitempty" json:"onFailure,omitempty"`
}
RolloutStep is one phase of a rollout.
func (RolloutStep) BakeDuration ¶
func (s RolloutStep) BakeDuration() (time.Duration, error)
BakeDuration parses Bake ("" = 0).
func (RolloutStep) EffectiveGuardrails ¶
func (s RolloutStep) EffectiveGuardrails(exp *Experiment) []MetricGoal
EffectiveGuardrails is what step s is judged on: the backing experiment's guardrails, each overridden by the step's guardrail on the same metric, then the step's other guardrails. An active rollout owns its experiment (the experiment loop skips it), so dropping the experiment's guardrails here would silently disable their auto-rollback. Ring-wide steps have no control arm and keep only their own (none: validated).
func (RolloutStep) EffectivePercent ¶
func (s RolloutStep) EffectivePercent() float64
EffectivePercent is Percent with the ring-wide default (100) applied.
func (RolloutStep) FailureAction ¶
func (s RolloutStep) FailureAction() string
FailureAction is OnFailure with its default.
func (RolloutStep) RingWide ¶
func (s RolloutStep) RingWide() bool
RingWide reports whether the step moves a ring pointer instead of experiment weights.
type RolloutStrategy ¶
type RolloutStrategy string
RolloutStrategy is how one step exposes the change.
const ( // StrategyProgressive moves Step.Ring's release pointer to Change.Release (client axis). StrategyProgressive RolloutStrategy = "progressive" // StrategyCanary gives Percent of the backing experiment's users the treatment. StrategyCanary RolloutStrategy = "canary" // StrategyBlueGreen switches Step.Ring from the pre-staged standby // Change.Release atomically; rollback switches back to Baseline.Release. StrategyBlueGreen RolloutStrategy = "blue-green" // StrategyDarkLaunch mirrors Percent of requests to the treatment via // halo-shadow; no user sees it (traffic axis). StrategyDarkLaunch RolloutStrategy = "dark-launch" // StrategyHoldout keeps Percent of users on control for Bake to measure // the long-term effect; everyone else gets the treatment. StrategyHoldout RolloutStrategy = "holdout" )
type Root ¶
type Root struct {
APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"`
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
Org string `yaml:"org" json:"org"`
// Identity configures OIDC for the portal, halo-proxy and enrollment (any IdP).
Identity Identity `yaml:"identity,omitempty" json:"identity,omitempty"`
// SelfService configures the developer portal (kiosk).
SelfService SelfService `yaml:"selfService,omitempty" json:"selfService,omitempty"`
// Workloads federates CI / cloud agents' OIDC tokens (ADR-0023).
Workloads *Workloads `yaml:"workloads,omitempty" json:"workloads,omitempty"`
// Agents governs delegated and remote (A2A) agents (ADR-0024).
Agents *Agents `yaml:"agents,omitempty" json:"agents,omitempty"`
// VendorAdmin drives the vendors' cloud admin consoles (ADR-0025).
VendorAdmin *VendorAdmin `yaml:"vendorAdmin,omitempty" json:"vendorAdmin,omitempty"`
// CloudAgents governs vendor-hosted background agents (ADR-0034).
CloudAgents *CloudAgents `yaml:"cloudAgents,omitempty" json:"cloudAgents,omitempty"`
// SupplyChain pins, scans and signs extension artifacts (ADR-0028).
SupplyChain *SupplyChain `yaml:"supplyChain,omitempty" json:"supplyChain,omitempty"`
// MCP is the approved MCP server registry (ADR-0029).
MCP *MCPRegistry `yaml:"mcp,omitempty" json:"mcp,omitempty"`
// Delegation is what units may add beyond the baseline (ADR-0018).
Delegation *Delegation `yaml:"delegation,omitempty" json:"delegation,omitempty"`
// Governance holds change-freeze windows and approval quorums (ADR-0030).
Governance *Governance `yaml:"governance,omitempty" json:"governance,omitempty"`
// Simple mode fields (tools, provider, models, ...): see simple.go.
Simple `yaml:",inline"`
}
Root is the halos.yaml document.
type RouteTarget ¶
type RouteTarget struct {
Upstream string `yaml:"upstream" json:"upstream"`
Model string `yaml:"model" json:"model"`
Weight float64 `yaml:"weight,omitempty" json:"weight,omitempty"`
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// TimeoutSeconds bounds connect + wait for response headers (default: the
// proxy's upstreamHeaderTimeout). Streams are not time-limited afterwards.
TimeoutSeconds int `yaml:"timeoutSeconds,omitempty" json:"timeoutSeconds,omitempty"`
}
RouteTarget is one provider target of a route. Targets sharing a Priority (lower first; default 0) form a tier: within a tier Weight splits traffic (sticky per identity+session) and the rest of the tier is the failover order; later tiers are tried only after earlier ones fail.
type RuleResult ¶ added in v0.4.0
type RuleResult struct {
Rule string `json:"rule"`
Need int `json:"need"`
Have int `json:"have"`
Groups []string `json:"groups"`
DistinctGroups bool `json:"distinctGroups,omitempty"`
Approvers []string `json:"approvers"`
Ticket string `json:"ticket,omitempty"` // the pattern the change's ticket must match
TicketOK bool `json:"ticketOK"`
Met bool `json:"met"`
}
RuleResult is one approval rule's state for a change.
type Sandbox ¶ added in v0.4.0
type Sandbox struct {
// Mode: off | workspace-write | read-only. Same field as permissions.sandbox
// (either may be set; ResolveProfile keeps the two equal).
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
// WritableRoots are absolute (or ~/) directories writable beyond the
// workspace, e.g. a build cache. A child list replaces its parent's, like
// allowedDomains; a unit adding one is a loosening.
WritableRoots []string `yaml:"writableRoots,omitempty" json:"writableRoots,omitempty"`
// ProtectedPaths may never be read or written by the agent: credentials
// (~/.ssh, ~/.aws) and secrets files (.env*). Absolute, ~/ or a
// workspace-relative glob. Unions down an extends chain and across units.
ProtectedPaths []string `yaml:"protectedPaths,omitempty" json:"protectedPaths,omitempty"`
}
Sandbox is the agent sandbox policy (ADR-0032): how far the harness's shell and tools may write, and which paths they may never touch.
type ScorecardGate ¶
type ScorecardGate struct {
// File is the scorecard JSON, relative to the policy dir.
File string `yaml:"file" json:"file"`
Variant string `yaml:"variant" json:"variant"`
// MinPass1 is the absolute pass@1 floor (0 = none).
MinPass1 float64 `yaml:"minPass1,omitempty" json:"minPass1,omitempty"`
// MaxRegression is the largest allowed pass-rate drop vs the scorecard's
// control (0.02 = 2pp); a significantly worse comparison always fails.
MaxRegression float64 `yaml:"maxRegression,omitempty" json:"maxRegression,omitempty"`
}
ScorecardGate passes when Variant's eval scorecard clears the bars.
type SelfService ¶
type SelfService struct {
Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// Launchers offered to developers: devcontainer | codespaces | coder | laptop.
Launchers []string `yaml:"launchers,omitempty" json:"launchers,omitempty"`
// Requestable items: kinds of access developers may request (approval opens a policy PR).
Requestable []string `yaml:"requestable,omitempty" json:"requestable,omitempty"` // mcp-server | model | ring-opt-in | harness
// Catalog lists MCP servers developers may request that aren't in their profile yet.
Catalog []MCPServer `yaml:"catalog,omitempty" json:"catalog,omitempty"`
// EnrollmentTTLSeconds bounds one-time laptop bootstrap tokens (default 900).
EnrollmentTTLSeconds int `yaml:"enrollmentTTLSeconds,omitempty" json:"enrollmentTTLSeconds,omitempty"`
}
SelfService configures the developer portal ("kiosk").
type ServerJSON ¶ added in v0.4.0
type ServerJSON struct {
Name string `json:"name"`
Version string `json:"version"`
Packages []struct {
RegistryType string `json:"registryType"`
Identifier string `json:"identifier"`
Version string `json:"version"`
Transport struct {
Type string `json:"type"`
} `json:"transport"`
PackageArguments []serverJSONArg `json:"packageArguments"`
RuntimeArguments []serverJSONArg `json:"runtimeArguments"`
EnvironmentVariables []struct {
Name string `json:"name"`
IsRequired bool `json:"isRequired"`
} `json:"environmentVariables"`
} `json:"packages"`
Remotes []struct {
Type string `json:"type"`
URL string `json:"url"`
Headers []struct {
Name string `json:"name"`
} `json:"headers"`
} `json:"remotes"`
}
ServerJSON is the part of the official MCP Registry's server.json (https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json) that `halo mcp registry import` reads.
type SignalSource ¶
type SignalSource string
SignalSource is where a registry metric's values come from.
const ( // SourceOnlineCLI: the harness's own OTEL export (metrics and log events), // attributed to a cohort by the halo.* resource attributes the profile sets. SourceOnlineCLI SignalSource = "online-cli" // SourceOnlineGateway: halo-proxy's per-request OTLP metrics. The gateway // assigns ring/variant itself, so it is the only online source for // traffic-axis experiments (the CLI never learns a gateway-assigned variant). SourceOnlineGateway SignalSource = "online-gateway" // SourceEval: offline replay (`halo eval`) only; never observed on live traffic. SourceEval SignalSource = "eval" // SourceOnlineEval: `halo eval online` LLM-judge grades of halo-shadow // pairs, received on the collector's authenticated eval receiver // (halo.source=eval). Pairs exist only for shadow experiments (and the // dark-launch steps of rollouts, which run their experiment as shadow). // Trusted for verdicts and PRs; never for the kill switch (gateway only). SourceOnlineEval SignalSource = "online-eval" )
type Simple ¶
type Simple struct {
// Tools enables harnesses, each pinned to an exact CLI version: a bare
// version, or {version, model} where model is one of the Models aliases
// that tool starts on instead of "default" (e.g. codex on an OpenAI model).
Tools map[string]ToolSpec `yaml:"tools,omitempty" json:"tools,omitempty"`
// Provider is where models run: a name (anthropic | bedrock | vertex |
// openai | gemini | multi) or {name, region, project, url}.
Provider *Provider `yaml:"provider,omitempty" json:"provider,omitempty"`
// Models maps an alias (what clients ask for; "default" is required) to a
// provider model id, or to a list of ids tried in failover order. An id may
// be prefixed "<provider>/" to pick another provider (required with multi).
Models map[string]ModelChoice `yaml:"models,omitempty" json:"models,omitempty"`
// Gateway is the base URL clients are pointed at (halo-proxy).
Gateway string `yaml:"gateway,omitempty" json:"gateway,omitempty"`
// GatewayEngine is what serves Gateway: halo-proxy (default), kong, or
// external (your own API gateway; clients are sent provider model ids).
GatewayEngine string `yaml:"gatewayEngine,omitempty" json:"gatewayEngine,omitempty"`
// Telemetry is the OTLP/HTTP endpoint (telemetry is always on).
Telemetry string `yaml:"telemetry,omitempty" json:"telemetry,omitempty"`
// Team is ring0: IdP groups, or user ids containing "@". Default:
// identity.adminGroups, else "<org>-ai-platform".
Team []string `yaml:"team,omitempty" json:"team,omitempty"`
// Safety preset: strict | standard (default) | relaxed. None bypasses permissions.
Safety string `yaml:"safety,omitempty" json:"safety,omitempty"`
// Rollout preset: fast | standard (default) | careful. Sets the rings and
// the step template `halo upgrade start` and `halo model switch --canary` use.
Rollout string `yaml:"rollout,omitempty" json:"rollout,omitempty"`
}
Simple is halos.yaml's simple mode: a handful of intent-level fields that Load expands, deterministically and in memory, into the Gateway, the "default" Profile and the preset Rings. Explicit documents of the same kind/name are overlaid on the expansion field by field (see overlay), so a repo can drop to full power one document at a time; `halo explain` prints the result and `halo eject` writes it out.
type StatusLine ¶ added in v0.3.0
type StatusLine struct {
Command string `yaml:"command,omitempty" json:"command,omitempty"`
Padding int `yaml:"padding,omitempty" json:"padding,omitempty"`
}
StatusLine is a managed status-line command.
type Stopping ¶
type Stopping struct {
// Method: msprt | fixed
Method string `yaml:"method" json:"method"`
Alpha float64 `yaml:"alpha,omitempty" json:"alpha,omitempty"`
MinSamples int `yaml:"minSamples,omitempty" json:"minSamples,omitempty"`
MaxDays int `yaml:"maxDays,omitempty" json:"maxDays,omitempty"`
MaxSpendUSD float64 `yaml:"maxSpendUSD,omitempty" json:"maxSpendUSD,omitempty"`
}
type Subject ¶
type Subject struct {
ID string
Groups []string
// Kind is "" for a human and SubjectWorkload for a CI / cloud workload.
Kind string
// Delegation is set when an agent acts for this subject (ADR-0024); ID,
// Groups and Kind are then the root principal's.
Delegation *DelegationChain
// Credential is set when the subject was verified from a credential
// halo-server signed (workload or delegated agent): what revocation
// (ADR-0026) is checked against.
Credential *CredentialRef
}
Subject is a user as seen by the control plane (from the IdP / auth gateway).
type SupplyChain ¶ added in v0.4.0
type SupplyChain struct {
// Scan: off | warn (default) | enforce. enforce turns scan findings into
// errors (as does `--strict`).
Scan string `yaml:"scan,omitempty" json:"scan,omitempty"`
// RequireSignatures refuses an artifact from outside the policy repo
// (git or oci) unless it carries a signature by a trusted identity.
RequireSignatures bool `yaml:"requireSignatures,omitempty" json:"requireSignatures,omitempty"`
Artifacts []Artifact `yaml:"artifacts,omitempty" json:"artifacts,omitempty"`
TrustedIdentities []TrustedIdentity `yaml:"trustedIdentities,omitempty" json:"trustedIdentities,omitempty"`
AllowFindings []AllowFinding `yaml:"allowFindings,omitempty" json:"allowFindings,omitempty"`
}
SupplyChain is the agent supply chain (ADR-0028): the skills, plugins, hooks, slash commands and agent definitions releases deliver, each pinned by a content digest, statically scanned, and optionally signature-checked.
type Telemetry ¶
type Telemetry struct {
Enabled bool `yaml:"enabled" json:"enabled"`
OTLPEndpoint string `yaml:"otlpEndpoint,omitempty" json:"otlpEndpoint,omitempty"`
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"` // grpc | http/protobuf
LogPrompts bool `yaml:"logPrompts,omitempty" json:"logPrompts,omitempty"`
Attributes map[string]string `yaml:"attributes,omitempty" json:"attributes,omitempty"`
}
type Toggle ¶
type Toggle struct {
Meta `yaml:",inline"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
// Owner is the team or person who answers for this toggle (required).
Owner string `yaml:"owner" json:"owner"`
// Expires (YYYY-MM-DD) is when the toggle should be removed; past it, validate warns.
Expires string `yaml:"expires,omitempty" json:"expires,omitempty"`
// Default is the state when no rule matches.
Default bool `yaml:"default" json:"default"`
// Axis: client = a settings fragment delivered in the signed release;
// traffic = a model route switch applied by the gateway.
Axis Axis `yaml:"axis" json:"axis"`
// Rules are evaluated in order; the first rule that matches decides.
Rules []ToggleRule `yaml:"rules,omitempty" json:"rules,omitempty"`
// Client is the payload of a client-axis toggle.
Client *ToggleClient `yaml:"client,omitempty" json:"client,omitempty"`
// Traffic is the payload of a traffic-axis toggle.
Traffic *ToggleTraffic `yaml:"traffic,omitempty" json:"traffic,omitempty"`
}
Toggle is a named switch with ordered targeting rules.
type ToggleClient ¶
type ToggleClient struct {
Harnesses map[string]TogglePatch `yaml:"harnesses" json:"harnesses"`
}
ToggleClient maps harness name to the fragment applied when the toggle is on.
type TogglePatch ¶
type TogglePatch struct {
MCPServers []MCPServer `yaml:"mcpServers,omitempty" json:"mcpServers,omitempty"`
Hooks []Hook `yaml:"hooks,omitempty" json:"hooks,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
// Overrides are harness-native keys, restricted to the same allowlist as profile overrides.
Overrides map[string]any `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}
TogglePatch is what an "on" client toggle adds to one harness's resolved profile. It goes through the same adapter and guardrails as a profile, so an unsupported part produces the adapter's warning, never a silent drop.
type ToggleRule ¶
type ToggleRule struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Rings []string `yaml:"rings,omitempty" json:"rings,omitempty"`
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
Users []string `yaml:"users,omitempty" json:"users,omitempty"`
// Percent (0-100) of the matching subjects, hashed with the toggle's own
// salt. Omitted = no percent condition; an explicit 0 matches nobody.
Percent *float64 `yaml:"percent,omitempty" json:"percent,omitempty"`
// Effect is the state when the rule matches: "on" (default) or "off".
Effect string `yaml:"effect,omitempty" json:"effect,omitempty"`
}
ToggleRule matches a subject when EVERY condition it sets holds (rings, groups and users each match if the subject is in any listed value). A rule with no condition matches everyone.
type ToggleTraffic ¶
type ToggleTraffic struct {
Routes map[string]ModelRoute `yaml:"routes" json:"routes"`
}
ToggleTraffic maps a gateway model alias to the route used while the toggle is on.
type ToolSpec ¶
type ToolSpec struct {
Version string `yaml:"version" json:"version"`
Model string `yaml:"model,omitempty" json:"model,omitempty"`
}
ToolSpec is one tool's pin and, optionally, its own default model alias.
type TrustedIdentity ¶ added in v0.4.0
type TrustedIdentity struct {
Name string `yaml:"name" json:"name"`
PublicKey string `yaml:"publicKey,omitempty" json:"publicKey,omitempty"`
CosignKey string `yaml:"cosignKey,omitempty" json:"cosignKey,omitempty"`
}
TrustedIdentity is a key artifact signatures may be made with. Exactly one of PublicKey (an ed25519 PEM file in the policy repo) and CosignKey (a cosign public key reference: file or KMS URI) is set.
type Unit ¶ added in v0.3.0
type Unit struct {
Meta `yaml:",inline"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
// Parent is the enclosing unit; empty for a top-level unit.
Parent string `yaml:"parent,omitempty" json:"parent,omitempty"`
// Owners approve changes to the unit's directory in the policy repo:
// CODEOWNERS entries such as @acme/payments-platform, @alice or an email.
Owners []string `yaml:"owners" json:"owners"`
// Members selects the unit's users from its parent's (or, at the top, the org's).
Members UnitMembers `yaml:"members" json:"members"`
// Overlay is what the unit changes on top of the baseline.
Overlay Overlay `yaml:"overlay,omitempty" json:"overlay,omitempty"`
// Dir is the repo-relative directory of the file that declares the unit
// (set by Load). The unit's owners own that directory.
Dir string `yaml:"-" json:"-"`
// File is the repo-relative file that declares the unit (set by Load). A
// top-level unit's file stays with the root owners: its members selector
// draws from the whole org.
File string `yaml:"-" json:"-"`
}
Unit is one business unit or team. Units nest through Parent; a user belongs to the deepest unit whose members selector, and every ancestor's, matches. The effective config is the ring's profile, then each ancestor's overlay (root first), then the unit's own. Enforced fields (see Loosenings) may only be tightened by an overlay; Validate rejects a loosening.
type UnitMembers ¶ added in v0.3.0
type UnitMembers struct {
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
Users []string `yaml:"users,omitempty" json:"users,omitempty"`
}
UnitMembers selects users by IdP group or user id (any match).
type Upstream ¶
type Upstream struct {
// URL is the base URL. Optional for kind vertex (derived from region).
URL string `yaml:"url,omitempty" json:"url,omitempty"`
// Kind: orchestrator | anthropic | bedrock | vertex | openai | azure-openai | gemini
Kind string `yaml:"kind" json:"kind"`
// Project is the Google Cloud project id for kind vertex.
Project string `yaml:"project,omitempty" json:"project,omitempty"`
// APIVersion is the api-version query for kind azure-openai; empty uses the
// versionless /openai/v1 surface.
APIVersion string `yaml:"apiVersion,omitempty" json:"apiVersion,omitempty"`
// Credential names where halo-proxy reads the provider key (kinds openai,
// azure-openai, gemini). Policy never holds the secret itself.
Credential *Credential `yaml:"credential,omitempty" json:"credential,omitempty"`
// Region is the AWS SigV4 region for kind bedrock. halo-proxy signs a
// bedrock upstream only if its host ends in an AWS suffix (.amazonaws.com,
// .amazonaws.com.cn, .api.aws) or is listed in the proxy's signHosts; it
// never signs for other hosts. The region is this value when set, else
// parsed from the host (e.g. bedrock-runtime.<region>.amazonaws.com).
Region string `yaml:"region,omitempty" json:"region,omitempty"`
// Serves lists the client wires an orchestrator upstream answers
// (anthropic-messages | bedrock-invoke | openai-responses | gemini). An
// orchestrator may translate, so validate only trusts what is declared here;
// other kinds' wires are known.
Serves []string `yaml:"serves,omitempty" json:"serves,omitempty"`
}
type Variant ¶
type Variant struct {
Name string `yaml:"name" json:"name"`
// Weight is the share of the experiment's users (weights are normalised).
Weight float64 `yaml:"weight" json:"weight"`
// Traffic axis: model alias -> route override.
Routes map[string]ModelRoute `yaml:"routes,omitempty" json:"routes,omitempty"`
// Client axis: profile to apply.
Profile string `yaml:"profile,omitempty" json:"profile,omitempty"`
Control bool `yaml:"control,omitempty" json:"control,omitempty"`
}
type VendorAdmin ¶ added in v0.3.0
type VendorAdmin struct {
// Ring whose profile the consoles get. Console settings are org-wide, so
// exactly one ring's profile can win; empty means the last ring (GA).
Ring string `yaml:"ring,omitempty" json:"ring,omitempty"`
Claude *VendorTarget `yaml:"claude,omitempty" json:"claude,omitempty"`
Codex *VendorTarget `yaml:"codex,omitempty" json:"codex,omitempty"`
Gemini *VendorTarget `yaml:"gemini,omitempty" json:"gemini,omitempty"`
Copilot *VendorTarget `yaml:"copilot,omitempty" json:"copilot,omitempty"`
Cursor *VendorTarget `yaml:"cursor,omitempty" json:"cursor,omitempty"`
}
VendorAdmin drives the vendors' cloud admin consoles from this policy (ADR-0025): `halo admin plan` diffs them, `halo admin apply` writes the vendors that have a public write API, and `halo admin export` writes the artifacts an admin pastes into the rest. A vendor is managed when its key is present.
type VendorTarget ¶ added in v0.3.0
type VendorTarget struct {
// Org is the tenant: the GitHub organization login (copilot, required),
// or the Claude organization / ChatGPT workspace / Cursor team id recorded
// in exported artifacts.
Org string `yaml:"org,omitempty" json:"org,omitempty"`
// Project is the Google Cloud project holding the Gemini Code Assist
// settings (gemini, required).
Project string `yaml:"project,omitempty" json:"project,omitempty"`
// Location of the Gemini settings resources (gemini; default global).
Location string `yaml:"location,omitempty" json:"location,omitempty"`
// ReleaseChannel pins Gemini Code Assist's release channel (gemini):
// stable | experimental. Empty leaves the channel unmanaged.
ReleaseChannel string `yaml:"releaseChannel,omitempty" json:"releaseChannel,omitempty"`
}
VendorTarget names the tenant one vendor's console settings apply to. Credentials never live here: `halo admin` reads them from a file or an environment variable named on its command line.
func (VendorTarget) GeminiLocation ¶ added in v0.3.0
func (t VendorTarget) GeminiLocation() string
GeminiLocation is Location with the default (global) applied.
func (VendorTarget) String ¶ added in v0.3.0
func (t VendorTarget) String() string
String names the target for logs and plan output (never a credential).
type WeeklyWindow ¶ added in v0.4.0
WeeklyWindow is "Fri 18:00" to "Mon 06:00": a day (Mon..Sun) and a 24-hour time. A To at or before From in the week ends the following week.
type WireFitness ¶
type WireFitness int
WireFitness is how well a model alias's route can answer a client wire.
const ( WireNo WireFitness = iota // no target can answer it (or the alias is unknown) WireMaybe // only orchestrators that do not declare it: they may translate WireYes // a target's provider (or a declaring orchestrator) answers it )
type WorkloadIssuer ¶ added in v0.3.0
type WorkloadIssuer struct {
Name string `yaml:"name" json:"name"`
// URL is the exact iss claim; discovery is <url>/.well-known/openid-configuration.
URL string `yaml:"url" json:"url"`
Audience string `yaml:"audience,omitempty" json:"audience,omitempty"`
// TenantClaim names the claim that identifies the tenant (org, group,
// account) a token was minted for; every rule of this issuer must match it
// exactly. Required for an issuer Halos does not know (see TenantClaims);
// for a known one it may only narrow the choice to one of its claims.
TenantClaim string `yaml:"tenantClaim,omitempty" json:"tenantClaim,omitempty"`
}
WorkloadIssuer is one trusted OIDC issuer (GitHub Actions, GitLab, ...).
func (WorkloadIssuer) TenantClaims ¶ added in v0.3.0
func (i WorkloadIssuer) TenantClaims() (provider string, claims []string)
TenantClaims returns the provider name ("" for an issuer Halos does not know) and the claims a rule of issuer i may pin its tenant with (none for an unknown issuer without tenantClaim). For a known issuer tenantClaim only narrows the provider's own tenant claims: a tenantClaim that is not one of them (ref, environment, ...) names no tenant and is ignored, so a rule pinning only it never counts as pinned (Validate reports it).
type WorkloadRule ¶ added in v0.3.0
type WorkloadRule struct {
Name string `yaml:"name" json:"name"`
Issuer string `yaml:"issuer" json:"issuer"` // WorkloadIssuer.Name
// Match: claim name -> exact value or path.Match glob ("*" never crosses "/").
// Every entry must hold; a missing claim never matches.
Match map[string]string `yaml:"match" json:"match"`
// Subject is the workload id; must start with "workload:". {claim}
// expands to that (matched) claim's value, e.g. workload:gh:{repository}.
Subject string `yaml:"subject" json:"subject"`
Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`
// MaxTTLSeconds bounds the minted credential (default 900, at most 3600).
MaxTTLSeconds int `yaml:"maxTTLSeconds,omitempty" json:"maxTTLSeconds,omitempty"`
// contains filtered or unexported fields
}
WorkloadRule maps matching tokens of one issuer to a workload subject.
func (WorkloadRule) TTL ¶ added in v0.3.0
func (r WorkloadRule) TTL() time.Duration
TTL is the rule's credential lifetime.
type Workloads ¶ added in v0.3.0
type Workloads struct {
// Audience CI tokens must carry (each issuer may override). Required.
Audience string `yaml:"audience" json:"audience"`
Issuers []WorkloadIssuer `yaml:"issuers" json:"issuers"`
// Rules are tried in order; the first whose every match entry holds wins.
Rules []WorkloadRule `yaml:"rules" json:"rules"`
// Experiments enrolls workloads in experiments like humans. Default false:
// workload traffic gets control routing and no shadow, so CI runs never
// skew experiment evidence.
Experiments bool `yaml:"experiments,omitempty" json:"experiments,omitempty"`
}
Workloads federates headless agents (CI jobs, cloud workloads) into the gateway: halo-server exchanges a token from a trusted OIDC issuer for a short-lived gateway credential when a rule matches its claims (ADR-0023).
func (*Workloads) AllowsExperiments ¶ added in v0.3.0
AllowsExperiments reports whether workload subjects may be enrolled in experiments (and shadowed).
func (*Workloads) AudienceFor ¶ added in v0.3.0
func (w *Workloads) AudienceFor(i WorkloadIssuer) string
AudienceFor is the aud a token from issuer i must carry.
func (*Workloads) IssuerByURL ¶ added in v0.3.0
func (w *Workloads) IssuerByURL(iss string) (WorkloadIssuer, bool)
IssuerByURL returns the trusted issuer whose URL is exactly iss.
func (*Workloads) MatchWorkload ¶ added in v0.3.0
func (w *Workloads) MatchWorkload(issuer string, claims map[string]any) (WorkloadRule, string, bool)
MatchWorkload returns the first rule for issuer whose match entries all hold on claims, plus its expanded subject. Claims are verified token claims. A rule that pins no tenant (UnpinnedRules) is skipped, whatever loaded it. A tombstone (TightenWorkloads) that matches ends the search with no rule.
func (*Workloads) UnpinnedRules ¶ added in v0.3.0
UnpinnedRules names the rules that pin no tenant of their issuer (or name no declared issuer). Validate refuses them; MatchWorkload never matches them, so a policy that reached a server without validation still cannot admit another tenant's jobs.
Source Files
¶
- agents.go
- budget.go
- channel.go
- cloudagents.go
- dlp.go
- egress.go
- governance.go
- guardrails.go
- identity.go
- load.go
- mcp.go
- mcpimport.go
- merge.go
- metrics.go
- metricsources.go
- names.go
- resolve.go
- rolebinding.go
- rollout.go
- simple.go
- snapshot.go
- supplychain.go
- toggle.go
- traffic_overlap.go
- types.go
- unit.go
- userid.go
- validate.go
- vendoradmin.go
- wires.go
- workloads.go