Documentation
¶
Overview ¶
Package githubpolicy syncs the cloud-hosted GitHub access-control policy down to the managed endpoint and evaluates classified GitHub activity against it locally.
The cloud authors GitHub policy as versioned, epoched rules. The managed endpoint fetches the snapshot, caches it by epoch/hash, and evaluates GitHub CLI / MCP / API activity locally. For ENG-426 the directive is observe-only: the engine records what it *would* allow or deny, but never blocks.
Precedence in the local engine is: general deterministic guardrails > this synced policy > probabilistic signals.
The contract mirrors packages/shared/src/github-policy-snapshot.ts in the kontext cloud repository.
Index ¶
Constants ¶
const ( ReasonCodeAllow = "ALLOWED_POLICY_CHECK" ReasonCodeDeny = "DENY_POLICY_CHECK" )
Reason codes shared with the cloud evaluator.
const ( ModeObserve = "observe" ModeEnforce = "enforce" )
Enforcement directive for the local engine.
- ModeObserve — evaluate every action and record a dry-run decision, but never block. Anything other than an explicit "enforce" is treated as observe.
- ModeEnforce — block denied actions. Reserved for a later, careful step; not emitted during the observer-mode pilot.
const ( LayerOrg = "org" LayerUser = "user" LayerAgent = "agent" )
Policy layers. Each rule binds a subject in exactly one layer.
const ( EffectAllow = "allow" EffectDeny = "deny" )
Rule effects.
const DefaultRefreshInterval = 60 * time.Second
DefaultRefreshInterval is how often the managed daemon re-fetches the snapshot. Hash-unchanged responses are cheap: they only bump freshness.
const SchemaVersion = "github-policy-snapshot-v1"
SchemaVersion identifies the snapshot wire format.
const SnapshotEndpoint = "/api/v1/policy/github/snapshot"
SnapshotEndpoint is the cloud path serving the policy snapshot. Tenancy is resolved from the install token; missing/unknown/revoked tokens get 401.
Variables ¶
This section is empty.
Functions ¶
func DefaultCachePathForDB ¶
DefaultCachePathForDB stores the snapshot next to the guard database, the same convention managedstream uses for its cursor state.
func DefaultIntervalFromEnv ¶
Types ¶
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache holds the per-installation snapshot in memory and mirrors it to disk so the policy survives daemon restarts and cloud outages.
func (*Cache) Apply ¶
Apply installs a freshly fetched snapshot. When the hash is unchanged the rules are not re-applied or re-persisted; only freshness is updated.
func (*Cache) LoadPersisted ¶
LoadPersisted primes the cache from disk so evaluation works before the first fetch completes. The persisted snapshot starts out stale until the cloud confirms it.
func (*Cache) MarkFetchFailed ¶
MarkFetchFailed records that the latest refresh failed; the cached snapshot keeps serving evaluations.
type Evaluation ¶
type Evaluation struct {
Request Request
Result string // "allow" | "deny"
ReasonCode string
Reason string
MatchedRules []MatchedRule
// DecidingRuleID is the rule that determined the outcome: the first
// matching deny, or the org-layer allow that anchored an allow. Empty
// when a layer vetoed by silence.
DecidingRuleID string
Mode string
Epoch int
Hash string
// Stale is true when the snapshot used was not confirmed by the cloud on
// the most recent refresh attempt.
Stale bool
// SubjectsResolved is false when no Kontext user/application identity was
// available, so user/agent-layer rules could not match their subject.
SubjectsResolved bool
}
Evaluation is the would-be decision for one request under one snapshot.
func Evaluate ¶
func Evaluate(snapshot Snapshot, status Status, request Request) (Evaluation, bool)
Evaluate mirrors the cloud evaluator exactly:
- A rule matches when its layer subject matches and each non-null dimension equals the request value exactly (null = wildcard, no globs).
- Any matching deny ⇒ deny, regardless of specificity.
- Otherwise allow only if every layer consents. The org layer is strict: it needs a matching allow. The user and agent layers abstain (consent) while the snapshot has zero rules in that layer; once a layer has any rule it is active and silence vetoes again.
The boolean result is false when the snapshot carries no rules at all (no active policy authored yet) — there is nothing to dry-run.
type GitContext ¶
GitContext carries repository context derived from the session working directory (current branch and configured remotes). It mirrors the toolInput.kontext.git enrichment the cloud classifier consumes.
func GitContextFromCWD ¶
func GitContextFromCWD(cwd string) GitContext
GitContextFromCWD derives the current branch and configured remotes from the session working directory. This stands in for the toolInput.kontext.git enrichment the cloud classifier receives.
type MatchedRule ¶
type MatchedRule struct {
ID string `json:"id"`
Layer string `json:"layer"`
Effect string `json:"effect"`
Decided bool `json:"decided,omitempty"`
}
MatchedRule records one rule that matched the request, and whether it was the rule that decided the outcome.
type ProviderAction ¶
type ProviderAction struct {
// Action is the canonical action name, e.g. "github.pr.write".
Action string
// Resource is the repository slug "owner/repo" when derivable.
Resource string
// BranchOrRef is the branch or ref when derivable (push refspecs,
// fetch/pull positionals, current branch fallback).
BranchOrRef string
}
ProviderAction is one classified GitHub action extracted from a tool call.
func ClassifyProviderActions ¶
func ClassifyProviderActions(toolName string, toolInput map[string]any, gitContext func() GitContext) []ProviderAction
ClassifyProviderActions maps a tool invocation to canonical GitHub actions. gitContext is invoked lazily — only when a git command needs remote or branch resolution — because deriving it shells out to git.
func ClassifyProviderActionsWithCWD ¶
func ClassifyProviderActionsWithCWD(toolName string, toolInput map[string]any, cwd string, gitContext func(string) GitContext) []ProviderAction
ClassifyProviderActionsWithCWD maps a tool invocation to canonical GitHub actions, using cwd as the base for git -C context resolution.
type Request ¶
type Request struct {
Action string
Resource string
BranchOrRef string
UserID string
ApplicationID string
}
Request is one classified GitHub action to evaluate. UserID and ApplicationID are the Kontext user and application subjects; the managed endpoint's trusted identity is the service account + installation, so both are typically empty (unresolved) and user/agent-layer rules then never match their subject.
type Rule ¶
type Rule struct {
ID string `json:"id"`
Layer string `json:"layer"`
// SubjectID is the org id, kontext user id, or application id depending
// on Layer.
SubjectID string `json:"subjectId"`
// ResourceID is a repository slug "owner/repo", or nil for any repository.
ResourceID *string `json:"resourceId"`
// ActionName is a canonical action name (e.g. "github.pr.write"), or nil
// for any action.
ActionName *string `json:"actionName"`
// BranchOrRef is a branch or ref constraint, or nil for any branch.
BranchOrRef *string `json:"branchOrRef"`
Effect string `json:"effect"`
// Specificity is higher for more pinned rules. Ordering/diagnostic hint
// only — evaluation is NOT most-specific-wins; any matching deny beats
// any matching allow.
Specificity int `json:"specificity"`
}
Rule is a single matchable rule. nil on ResourceID / ActionName / BranchOrRef means "matches any" for that dimension; non-nil dimensions are exact string matches. For the first GitHub pilot, policy matching uses repo (ResourceID) + branch (BranchOrRef) + action; file path and API endpoint details are audit context only.
type Snapshot ¶
type Snapshot struct {
SchemaVersion string `json:"schemaVersion"`
OrganizationID string `json:"organizationId"`
// ProviderID is the GitHub provider id, or nil when the org has no GitHub
// provider configured.
ProviderID *string `json:"providerId"`
ProviderKey string `json:"providerKey"`
Mode string `json:"mode"`
// Epoch is the active policy epoch; 0 when the org has no active policy.
Epoch int `json:"epoch"`
// Hash is a stable content hash over {epoch, rules}, independent of
// GeneratedAt, so an unchanged policy keeps an unchanged hash.
Hash string `json:"hash"`
Rules []Rule `json:"rules"`
GeneratedAt string `json:"generatedAt"`
}
Snapshot is the response body of GET /api/v1/policy/github/snapshot. The server resolves the organization from the install token; there is no organization parameter.
type SnapshotProvider ¶
SnapshotProvider is what the guard runtime consumes: the current snapshot, its freshness, and whether a snapshot exists at all.
type Status ¶
type Status struct {
// FetchedAt is when the cached snapshot was last confirmed by the cloud
// (fetch succeeded, whether or not the hash changed).
FetchedAt time.Time `json:"fetched_at"`
// Stale is true when the most recent fetch attempt failed and the cache
// is serving the last known snapshot.
Stale bool `json:"stale"`
// LastError holds the most recent fetch failure, if any.
LastError string `json:"last_error,omitempty"`
}
Status describes how trustworthy the cached snapshot currently is. A stale snapshot keeps being evaluated — stale-but-deterministic beats unavailable — and the staleness is recorded on every decision.