delegation

package
v0.4.16 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package delegation implements the github-repository-delegation-v1 control-plane contract described by github/gh-aw-firewall ADR 0001 ("Agent enclave repository admission"). It lets AWF create/confirm and revoke short-lived, invocation-scoped mcpg identities for dynamically admitted agent enclaves without adding, removing, or mutating any configured MCP backend, route, or tool.

Index

Constants

View Source
const EnvControlCapabilityKey = "MCP_GATEWAY_DELEGATION_CONTROL_KEY"

EnvControlCapabilityKey is the environment variable holding the AWF-only delegation-control capability value minted by the compiler at startup. Primary and enclave agents must never receive this value: it is only ever read by the process hosting the controller and compared against inbound requests on the private awf-enclave-mcp-control channel.

View Source
const EnvControlListenAddr = "MCP_GATEWAY_DELEGATION_CONTROL_LISTEN"

EnvControlListenAddr is the private listener address for the AWF control channel. It must not be shared with the executor-facing proxy listener.

View Source
const ToolPolicyGitHubRepositoryReadV1 = "github-repository-read-v1"

ToolPolicyGitHubRepositoryReadV1 is the only delegated tool policy this package understands: a closed allowlist of repository-scoped read-only GitHub tools (list_issues and issue_read).

Variables

This section is empty.

Functions

func DelegatedTools

func DelegatedTools() []string

DelegatedTools returns the exact, closed set of tools permitted by github-repository-read-v1. Each call allocates a fresh slice; callers on a hot path (such as per-call authorization checks) should use IsDelegatedTool instead.

func IsCanonicalRepositorySelector

func IsCanonicalRepositorySelector(selector string) bool

IsCanonicalRepositorySelector reports whether selector is already the exact canonical ASCII byte sequence required by the ADR:

^[a-z0-9](?:[a-z0-9-]{0,38})/(?!\.\.?$)(?!.*\.\.)[a-z0-9._-]{1,100}$

There is no trimming, case folding, Unicode normalization, URL decoding, or alternate syntax: callers must reject any selector for which this returns false rather than attempt to normalize it.

func IsDelegatedTool

func IsDelegatedTool(tool string) bool

IsDelegatedTool reports whether tool is a member of the closed github-repository-read-v1 tool set, without allocating.

Types

type AuditEvent

type AuditEvent struct {
	Operation      string // "create_or_confirm", "confirm", "revoke", "revoke_by_labels", "expire", "reconcile"
	RunIDHash      string
	EnclaveEntryID string
	InvocationID   string
	RepositoryHash string
	HandleHash     string
	PolicyGen      uint64
	Outcome        string // "admitted", "confirmed", "denied", "revoked", "mismatch", "expired"
	Reason         string // coarse, non-identifying reason code only
}

AuditEvent is a redacted lifecycle record for one delegation-control operation. Per the ADR, EnclaveEntryID and InvocationID are structural correlation labels the compiler already assigns and are retained in the clear so operators can reconcile a run's dynamic state; they never carry a repository name, credential, or header. Every value that could disclose an identity, a repository selector, or an idempotency key (RunIDHash, RepositoryHash, HandleHash) is hashed before it reaches this struct, so AuditEvent as a whole never carries a bearer value, header, or unredacted private repository name.

type ControlCapability

type ControlCapability struct {
	// contains filtered or unexported fields
}

ControlCapability authenticates inbound control-plane requests against the single AWF-only capability value installed for this run. It intentionally has no relationship to per-invocation Claims verified by internal/enclavegithub: that verifier authenticates enclave executors to the GitHub read proxy, while ControlCapability authenticates only AWF to the delegation controller itself.

func NewControlCapability

func NewControlCapability(secret string) (*ControlCapability, error)

NewControlCapability creates a capability verifier from a non-empty secret value. The secret is never retained in comparable form: only its SHA-256 digest is stored, so a leaked process memory dump of this struct cannot be used to recover or forge the capability value.

func (*ControlCapability) Authenticate

func (c *ControlCapability) Authenticate(authorizationHeader string) error

Authenticate verifies an Authorization header value against the capability. It accepts the header value with a leading Bearer-scheme prefix (stripped before comparison) or as a bare value, per MCP spec 7.1. Comparison is constant-time over fixed-size digests to avoid leaking the capability's length or contents through timing.

type CreateOrConfirmRequest

type CreateOrConfirmRequest struct {
	// RunID must equal the envelope's bound workflow run.
	RunID string `json:"run_id"`
	// EnclaveBackend must equal the envelope's single AWF enclave backend.
	EnclaveBackend string `json:"enclave_backend"`
	// EnclaveEntryID identifies the enclave entry (frontmatter block) this
	// invocation belongs to.
	EnclaveEntryID string `json:"enclave_entry_id"`
	// InvocationID identifies one bounded enclave invocation.
	InvocationID string `json:"invocation_id"`
	// Repository is the canonical, exact-byte owner/repo selector chosen
	// for this invocation. It must already be canonical: the Store performs
	// no trimming, case folding, Unicode normalization, or URL decoding.
	Repository string `json:"repository"`
	// ToolPolicy must equal ToolPolicyGitHubRepositoryReadV1.
	ToolPolicy string `json:"tool_policy"`
	// SchemaHash is the finite response schema hash approved for this
	// invocation; it must be a member of the envelope's allowed set.
	SchemaHash string `json:"schema_hash"`
	// AdmittedDefaultBranchSHA is the default-branch SHA AWF resolved
	// during live-read admission, when known at request time.
	AdmittedDefaultBranchSHA string `json:"admitted_default_branch_sha,omitempty"`
	// RequestedTTL bounds how long the identity should live; it is capped
	// by (and must not exceed) the envelope's MaxIdentityTTL.
	RequestedTTL time.Duration `json:"requested_ttl"`
	// InvocationExpiresAt is the absolute deadline of the invocation, when
	// one is supplied by AWF. An identity can never outlive this deadline.
	InvocationExpiresAt time.Time `json:"invocation_expires_at,omitempty"`
	// IdempotencyKey deduplicates retried create/confirm calls for the same
	// (RunID, EnclaveEntryID, InvocationID, Repository) tuple.
	IdempotencyKey string `json:"idempotency_key"`
}

CreateOrConfirmRequest is an AWF-authenticated request to create or confirm exactly one delegated identity. Every field must be a strict subset of the compiler-installed Envelope; the Store rejects anything wider than the envelope allows.

type Envelope

type Envelope struct {
	// RunID is the workflow run this envelope, and every identity minted
	// from it, is bound to.
	RunID string `json:"run_id"`
	// EnclaveBackend is the single AWF enclave backend identities may be
	// bound to.
	EnclaveBackend string `json:"enclave_backend"`
	// AllowedRepositories is the closed set of canonical owner/repo
	// selectors the compiler admitted for this run. Selectors are compared
	// as exact ASCII byte sequences; no normalization is performed.
	AllowedRepositories []string `json:"allowed_repositories"`
	// ToolPolicy is the single delegated tool policy this envelope allows.
	// Only ToolPolicyGitHubRepositoryReadV1 is currently supported.
	ToolPolicy string `json:"tool_policy"`
	// AllowedSchemaHashes is the closed set of finite response schema
	// hashes the compiler approved for this run.
	AllowedSchemaHashes []string `json:"allowed_schema_hashes"`
	// MaxIdentityTTL bounds how long any single delegated identity may
	// live, and therefore how long an executor bearer remains valid.
	MaxIdentityTTL time.Duration `json:"max_identity_ttl"`
	// ExpiresAt is the envelope's own absolute expiry, no later than the
	// workflow job lifetime. No identity may be created once the envelope
	// itself has expired.
	ExpiresAt time.Time `json:"expires_at"`
}

Envelope is the compiler-installed, compiler-bounded policy envelope bootstrapped into the controller at gateway startup. Every delegated identity must be a strict subset of this envelope; the controller rejects any request for a server, tool, repository, credential, backend URL, or guard policy outside it. The compiler is not a runtime service: once installed the envelope is immutable for the lifetime of the process.

func (*Envelope) AllowsRepository

func (e *Envelope) AllowsRepository(repo string) bool

AllowsRepository reports whether repo is an exact-byte member of the envelope's admitted repository set.

func (*Envelope) AllowsSchemaHash

func (e *Envelope) AllowsSchemaHash(schemaHash string) bool

AllowsSchemaHash reports whether schemaHash is an exact-byte member of the envelope's admitted schema hash set.

func (*Envelope) Validate

func (e *Envelope) Validate() error

Validate checks the envelope's own invariants. It does not check any per-request binding; use Envelope.Admits for that.

type Identity

type Identity struct {
	Handle                   string    `json:"handle"`
	ExecutorBearer           string    `json:"executor_bearer"`
	RunID                    string    `json:"run_id"`
	EnclaveBackend           string    `json:"enclave_backend"`
	EnclaveEntryID           string    `json:"enclave_entry_id"`
	InvocationID             string    `json:"invocation_id"`
	Repository               string    `json:"repository"`
	ToolPolicy               string    `json:"tool_policy"`
	SchemaHash               string    `json:"schema_hash"`
	AdmittedDefaultBranchSHA string    `json:"admitted_default_branch_sha,omitempty"`
	ExpiresAt                time.Time `json:"expires_at"`
	InvocationExpiresAt      time.Time `json:"invocation_expires_at,omitempty"`
	PolicyGeneration         uint64    `json:"policy_generation"`
	IdempotencyKey           string    `json:"idempotency_key"`
	CreatedAt                time.Time `json:"created_at"`
	Revoked                  bool      `json:"revoked"`
}

Identity is one invocation-scoped delegated identity bound to a single canonical repository under github-repository-read-v1.

type IdentityResult

type IdentityResult struct {
	Handle                   string    `json:"handle"`
	ExecutorBearer           string    `json:"executor_bearer"`
	Repository               string    `json:"repository"`
	ToolPolicy               string    `json:"tool_policy"`
	Tools                    []string  `json:"tools"`
	AdmittedDefaultBranchSHA string    `json:"admitted_default_branch_sha,omitempty"`
	ExpiresAt                time.Time `json:"expires_at"`
}

IdentityResult is returned to AWF on a successful create or confirm call. It intentionally excludes any field not required by the executor: no credentials, headers, or repository contents are ever included.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store atomically creates/confirms and revokes delegated identities for one compiler-installed Envelope. It is safe for concurrent use: every mutating operation is serialized so that concurrent create/confirm/revoke calls for the same idempotency key are atomic and generation-safe.

func LoadStore

func LoadStore(path string, envelope *Envelope, generation uint64) (*Store, error)

LoadStore reconstructs a Store from a prior SaveState file. If path does not exist, this is a fresh start (no prior state to reconcile) and an empty, fully-reconciled Store is returned. If path exists but is corrupt, truncated, or fails checksum verification, an empty Store is returned with recoveryIncomplete set: callers must fail closed for new dynamic admissions until MarkReconciled is called after an operator confirms outstanding identities are safe to disregard.

Identities that already expired at load time are dropped silently: their absence is ordinary lifecycle behavior, not incomplete reconstruction.

func NewStore

func NewStore(envelope *Envelope, generation uint64) (*Store, error)

NewStore creates an empty Store bound to envelope. generation is the compiler-supplied policy generation for this envelope installation; it is stamped onto every identity minted from this Store so a later envelope replacement can be distinguished from stale identities.

func (*Store) Authorize

func (s *Store) Authorize(executorBearer, runID, enclaveBackend, repository, tool string) error

Authorize enforces that executorBearer is a live identity bound to exactly the given run, enclave backend, repository, and tool. It rejects replayed, expired, revoked, wrong-repository, wrong-tool, and wrong-run identities; none of those can establish or continue a session.

func (*Store) AuthorizeExecutor

func (s *Store) AuthorizeExecutor(executorBearer, repository, tool string) error

AuthorizeExecutor authorizes an executor bearer against the repository and tool derived by the data-plane route. The run and backend are read from the authenticated identity rather than supplied by the executor.

func (*Store) CreateOrConfirm

func (s *Store) CreateOrConfirm(req CreateOrConfirmRequest) (*IdentityResult, error)

CreateOrConfirm atomically creates a new delegated identity, or confirms an existing one for the same idempotency scope. A repeated call for the same (run, enclave entry, invocation, idempotency key) tuple returns the exact same binding, expiry, admitted default-branch SHA, opaque handle, and executor bearer. A request whose binding differs from the stored identity is terminal: the stored identity is revoked and an error is returned.

func (*Store) IsRecoveryIncomplete

func (s *Store) IsRecoveryIncomplete() bool

IsRecoveryIncomplete reports whether restart reconstruction left this Store unable to vouch for prior state. Callers must fail closed for new dynamic admissions while this is true.

func (*Store) MarkReconciled

func (s *Store) MarkReconciled()

MarkReconciled clears the recovery-incomplete flag once an operator or automated process has confirmed outstanding state is safe to resume from.

func (*Store) Revoke

func (s *Store) Revoke(handle string) error

Revoke idempotently revokes the identity bound to handle. Revoking an unknown or already-revoked handle is not an error.

func (*Store) RevokeByLabels

func (s *Store) RevokeByLabels(runID, enclaveEntryID string) int

RevokeByLabels idempotently revokes every identity bound to (runID, enclaveEntryID) and returns how many identities were revoked. It is used both for explicit label-scoped revocation and for shutdown/reconciliation cleanup.

func (*Store) SaveState

func (s *Store) SaveState(path string) error

SaveState persists every currently live identity to path so the controller can reconstruct labelled live delegations after a restart. The file is written with 0600 permissions because it contains executor bearer secrets. A trailing SHA-256 checksum lets LoadStore detect truncation or corruption and fail closed instead of silently reconstructing partial state.

func (*Store) Snapshot

func (s *Store) Snapshot() map[string]Identity

Snapshot returns a defensive copy of every currently live (non-expired, non-revoked) identity, keyed by handle. It is used for state persistence ahead of restart recovery.

Jump to

Keyboard shortcuts

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