Documentation
¶
Overview ¶
Package api wires Wardyn's control-plane REST surface (the wardynd binary). It contains ZERO target-specific code (the parity rule): it talks to runners only through the runner.Runner interface, and to identity/secrets/broker only through their contract interfaces. Every security decision fails closed.
Route map (see the REST contract in the architecture brief):
Public (admin bearer):
POST /api/v1/runs ; GET /api/v1/runs ; GET /api/v1/runs/{id}
GET /api/v1/runs/{id}/grants
POST /api/v1/runs/{id}/kill
GET /api/v1/runs/{id}/attach (WebSocket: interactive PTY)
GET /api/v1/approvals?state= ; POST /api/v1/approvals/{id}/approve|deny
GET /api/v1/audit?run_id=
POST /api/v1/policies ; GET /api/v1/policies ; GET /api/v1/policies/{id}
PUT /api/v1/policies/{id} ; DELETE /api/v1/policies/{id}
GET /healthz
Internal (run-token bearer, identity.Provider.Verify aud="wardyn-internal"):
POST /api/v1/internal/decisions
POST /api/v1/internal/approvals ; GET /api/v1/internal/approvals/{id}
POST /api/v1/internal/credentials/mint
Ground-truth (host-sensor bearer, identity.Provider.Verify aud="wardyn-groundtruth"):
POST /api/v1/internal/groundtruth (eBPF/Tetragon kernel-event batch)
Index ¶
- func LoadPolicySpec(path string) (types.RunPolicySpec, error)
- func NewManagedCredProvider(store secretstore.Store, provider string) subscription.Provider
- type ApprovalService
- type AuditSpool
- type ComponentInfo
- type ComposerBackendReadiness
- type Config
- type ImageBuilder
- type MintBroker
- type RecordTaskResult
- type Server
- type SetupAgeKey
- type SetupAuth
- type SetupBedrock
- type SetupCheck
- type SetupComposer
- type SetupDeployment
- type SetupFix
- type SetupHarness
- type SetupItem
- type SetupPlatform
- type SetupProvider
- type SetupRunner
- type SetupSecrets
- type SetupStatus
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LoadPolicySpec ¶
func LoadPolicySpec(path string) (types.RunPolicySpec, error)
LoadPolicySpec reads and validates a RunPolicySpec from a JSON file. Used by wardynd to seed the default policy from examples/policies/default.json.
func NewManagedCredProvider ¶ added in v0.2.0
func NewManagedCredProvider(store secretstore.Store, provider string) subscription.Provider
NewManagedCredProvider builds a managed subscription provider over store for a provider id (e.g. "anthropic"). Returns nil when store is nil (managed mode simply unavailable).
Types ¶
type ApprovalService ¶
type ApprovalService interface {
Request(ctx context.Context, req types.ApprovalRequest) (types.ApprovalRequest, error)
Decide(ctx context.Context, id uuid.UUID, approve bool, decidedByType types.ActorType, decidedBy, reason string) (types.ApprovalRequest, error)
Get(ctx context.Context, id uuid.UUID) (types.ApprovalRequest, error)
List(ctx context.Context, state types.ApprovalState) ([]types.ApprovalRequest, error)
}
ApprovalService is the narrow approval FSM surface the API depends on. It is satisfied by package-level wrappers over internal/approval (see wardynd wiring), keeping the API decoupled from concrete storage.
type AuditSpool ¶
type AuditSpool struct {
// contains filtered or unexported fields
}
AuditSpool durably records audit events whose PRIMARY store write failed, so a security event is never silently lost (C1). The control-plane audit log is the system of record; silently dropping a credential.mint / run.kill / egress.deny event when the database blips would let a run proceed while reporting success — exactly the failure a governance tool must not have.
It is a mutex-guarded append-only JSONL file: intentionally simple, fit for the local-first single-host deployment, and lets an operator (or a future drain job) replay spooled events into the audit log once the store recovers. A nil *AuditSpool disables spooling (a failed primary write is then only logged loudly).
func NewAuditSpool ¶
func NewAuditSpool(path string) (*AuditSpool, error)
NewAuditSpool opens (creating if needed) an append-only JSONL spool at path.
func (*AuditSpool) Append ¶
func (a *AuditSpool) Append(ev types.AuditEvent) error
Append writes one event as a single JSON line and fsyncs it, so a crash right after a primary-write failure still preserves the event. It returns an error only when even the fallback write fails (the last-resort signal the event is lost).
type ComponentInfo ¶
type ComponentInfo struct {
Selected string `json:"selected"`
RecommendedProduction string `json:"recommended_production,omitempty"`
Source string `json:"source,omitempty"`
}
ComponentInfo describes one pluggable seam's selection for /healthz. Selected is ALWAYS the actual running implementation; RecommendedProduction is the standard Wardyn recommends converging to and may differ from Selected (and even from the shipped default — the honest recommended-vs-shipped split documented in docs/PLUGGABILITY.md). Source is "default" or "configured".
type ComposerBackendReadiness ¶
type ComposerBackendReadiness struct {
Name string `json:"name"`
Provider string `json:"provider"`
Model string `json:"model"`
Wire string `json:"wire"`
Transport string `json:"transport,omitempty"` // normalized (HTTP wires => "api"); cli tool / fake variant
Auth string `json:"auth,omitempty"` // openai azure only: apikey|entra
Enabled bool `json:"enabled"`
NeedsKey bool `json:"needs_key"`
KeySecret string `json:"key_secret,omitempty"`
KeyResolved bool `json:"key_resolved"`
}
ComposerBackendReadiness is the boot-snapshot readiness of one configured composer backend. KeySecret is a secret NAME (never a value); KeyResolved is whether that secret (or the env fallback) was present at boot.
type Config ¶
type Config struct {
// Store is the abstract persistence seam (run/policy/grant/approval/audit
// CRUD + reads). The control plane talks to this instead of *pgxpool.Pool
// directly, so a future pure-Go backend can be swapped in. Defaults to a
// store.NewPG(Pool) adapter when wired from wardynd.
Store store.Store
// Identity mints/verifies/revokes per-run identities (embedded by default).
Identity identity.Provider
// Approvals is the approval FSM service.
Approvals ApprovalService
// Broker mints credentials inside the approval-gated transaction.
Broker MintBroker
// Audit records control-plane-originated audit events. The recorder handed in
// is the shared masking → spooling → store/fanout chain (see cmd/wardynd), so a
// failed durable write is masked, logged loudly, and spooled to the local
// append-only fallback for EVERY writer — the API layer no longer spools itself.
Audit audit.Recorder
// Runner launches sandboxes. Nil => headless API-only mode.
Runner runner.Runner
// AdminToken gates the public API (constant-time bearer compare). Empty
// disables the public API entirely (fail closed) except /healthz.
AdminToken string
// LocalMode enables LOCAL HOST MODE: the public-API auth (humanOrAdminAuth)
// is bypassed entirely and every admin-gated action is attributed to
// LocalOperator. This is the single-developer localhost path — no SSO, no
// token, no Dex. It NEVER affects internalAuth (sidecar/run-token
// verification), so the sidecar callback path is unchanged. The daemon
// (cmd/wardynd) refuses LocalMode when bound to an EXPLICIT public IP, but
// only WARNS (does not refuse) on an unspecified bind (0.0.0.0, the
// WARDYN_LISTEN default) — operators must bind/publish loopback-only for a
// real guarantee (the Compose default already publishes 127.0.0.1).
LocalMode bool
// LocalOperator is the principal stamped on runs/approvals/audit in
// LocalMode (e.g. "local:<os-user>"). Ignored unless LocalMode is true.
LocalOperator string
// TrustDomain is surfaced in /healthz and used for run SPIFFE ids.
TrustDomain string
// DefaultPolicy is applied to runs created without an explicit policy_id.
DefaultPolicy types.RunPolicySpec
// RunnerTarget records which target a run is dispatched to ("docker"|"k8s"),
// or "none" for a headless control plane (-runner none: runs stay PENDING).
// Defaults to "docker".
RunnerTarget string
// UIDir, when set, serves a SPA from this directory at "/".
UIDir string
// ControlPlaneURL is the externally-reachable base URL handed to sidecars
// (proxy config) so they can call the internal endpoints.
ControlPlaneURL string
// ProxyURL, when set, overrides the WARDYN_PROXY_URL injected into sandbox
// env. Defaults to "http://wardyn-proxy:3128" (the per-run proxy sidecar
// hostname set by the docker driver). Non-secret: it is a network address,
// not a credential.
ProxyURL string
// RecordingStore, when set, serves PTY session replays under
// GET /api/v1/runs/{id}/recording/{id} (admin-gated) and accepts uploads
// via PUT /api/v1/runs/{id}/recording (run-token auth).
RecordingStore recording.Store
// OIDC, when set, enables human SSO: it mounts /auth/login,/auth/callback,
// /auth/logout and composes oidc.Middleware in front of the admin-gated API
// so a valid session cookie OR the admin bearer token authenticates a caller.
// The admin token still works for the CLI when OIDC is configured.
OIDC *oidc.Authenticator
// ImageBuilder, when set, builds a per-run sandbox image from the
// devcontainer_repo in a create-run request. Nil disables devcontainer
// builds (the request degrades to the convention image).
ImageBuilder ImageBuilder
// AgentImages, when set, is an agent-name -> OCI image-ref map that
// overrides the ghcr convention image for named agents. Agents not present
// in the map fall back to the convention. Validated at server construction
// (must parse if set); nil disables the override and the convention is used
// for every agent.
AgentImages map[string]string
// AgentAnthropicModel, when set, pins the ANTHROPIC_MODEL env inside a
// claude-code sandbox (e.g. "opus") so the agent uses a specific model rather
// than the account/CLI default (which a promo can push to a cheaper model like
// Fable). Empty = unset; the CLI's own default is used. Applies in both
// subscription and api-key auth modes.
AgentAnthropicModel string
// BedrockRegion / BedrockModel, when BOTH set, opt a claude-code run into the
// Amazon Bedrock Anthropic transport (CLAUDE_CODE_USE_BEDROCK) instead of the
// default api-key/proxy-inject path — an enterprise path with no direct
// Anthropic egress, billed via AWS. BedrockModel is a Bedrock model id (a
// cross-region inference-profile id like "us.anthropic.claude-..." is what
// claude-code actually expects, not a bare foundation-model id). Boot-time
// config only (mirrors AgentAnthropicModel — no live admin write path); the
// AWS credentials themselves come from the secret store (aws-access-key-id /
// aws-secret-access-key / optional aws-session-token), read directly at
// dispatch time because Bedrock's AWS SigV4 request signing can't be
// proxy-injected the way a static x-api-key header can (see runs.go
// resolveBedrockAuth). Empty BedrockRegion or BedrockModel disables Bedrock
// entirely; a subscription-mode run always takes priority over Bedrock.
BedrockRegion string
BedrockModel string
// BedrockAWSConfigDir, when set, bind-mounts a host AWS config directory
// (a `~/.aws`) READ-ONLY into the sandbox at /home/agent/.aws, so the AWS
// SDK inside the run resolves credentials itself — including short-lived AWS
// SSO / IAM Identity Center sessions, which it refreshes on demand. This is
// the HOST-MODE alternative to pasting static aws-access-key-id/-secret
// secrets (which expire under SSO and must be re-pasted): with the mount,
// `aws sso login` on the host is enough and nothing is stored in Wardyn.
// It is OFF by default and only set by the host-mode installer (run-host.sh
// / setup.sh) — a team/compose deployment never sets it, so a shared service
// never mounts an operator's ambient cloud credentials (invariant 1). It is
// the deliberate host-mode residency tradeoff already accepted for the
// ~/.claude subscription mount. Empty = disabled. Takes precedence over the
// resident static-key path but not over a bedrock-api-key bearer.
BedrockAWSConfigDir string
// BedrockAWSProfile, when set, is passed as AWS_PROFILE into the sandbox so
// the SDK selects a named profile from the mounted config (common with SSO:
// `aws sso login --profile X`). Only meaningful with BedrockAWSConfigDir.
BedrockAWSProfile string
// BedrockAWSSSORegion is the AWS region whose SSO endpoints (oidc.<r>,
// portal.sso.<r>) the sandbox is allowed to reach so the SDK can exchange an
// SSO token for role credentials. It often differs from BedrockRegion.
// Empty defaults to BedrockRegion. Only meaningful with BedrockAWSConfigDir.
BedrockAWSSSORegion string
// Secrets is the at-rest secret store. It backs the admin secret-management
// endpoints (PUT/DELETE/list — values are NEVER readable via the API) and
// the internal injection-resolve endpoint the proxy calls at startup. Nil
// disables both surfaces.
Secrets secretstore.Store
// MaskRegistry, when non-nil, is used to mask verbatim secret values from
// PTY capture / asciicast uploads before they reach the RecordingStore.
// A nil registry disables masking (existing tests stay green).
MaskRegistry *secretmask.Registry
// SubscriptionToken, when non-nil, yields the operator's LIVE Anthropic
// subscription OAuth access token from the resident ~/.claude credentials.
// The internal injection-resolve endpoint uses it to inject a fresh token
// per request for subscription runs (secret name subscriptionOAuthSecret),
// so the sandbox holds only an inert sentinel instead of a copy that goes
// stale. Nil disables the subscription-injection path (falls back to the
// resident-copy behavior).
SubscriptionToken subscription.Provider
// ManagedToken, when non-nil, yields the Wardyn-MANAGED Anthropic subscription
// token — a long-lived `claude setup-token` the operator captured via the
// container-login flow, stored age-encrypted. The injection sink resolves the
// types.ManagedOAuthSecret sentinel through it, exactly like SubscriptionToken
// resolves the resident-host sentinel. This is what credentials a subscription
// run in a COMPOSE deployment whose distroless wardynd has no host ~/.claude.
// Nil disables the managed-injection path.
ManagedToken subscription.Provider
// DisableSubscriptionInject is the operator ESCAPE HATCH: when true (env
// WARDYN_SUBSCRIPTION_INJECT=off), subscription runs keep the legacy
// resident-copy behavior (the mounted credential, which can go stale) instead
// of auto-enabling TLS-MITM + injecting the live host token. Default false =
// the safe proxy-side default whenever a SubscriptionToken provider is wired.
DisableSubscriptionInject bool
// Now is overridable in tests; defaults to time.Now.
Now func() time.Time
// BaseCtx is the process-lifetime base context used for detached background
// work that MUST outlive the request that started it — specifically the
// completion watcher dispatch starts after Exec. The request/dispatch ctx is
// cancelled when the HTTP handler returns, which would kill a watcher
// immediately; BaseCtx (threaded from main.go's rootCtx) keeps it alive for
// the lifetime of the daemon and is cancelled on shutdown. Defaults to
// context.Background() when unset (the watcher then only stops on process
// exit).
BaseCtx context.Context
// Composer, when set and Enabled(), powers the AI Run Composer endpoints
// (POST /api/v1/runs/compose, GET /api/v1/composer/backends): a registry of
// LLM backends turns a natural-language task description into a PROPOSED
// {run, inline_policy} that Wardyn risk-grades deterministically and clamps to
// DefaultPolicy before returning for human approval. Nil / not-Enabled
// disables the endpoints (404), so the feature is strictly opt-in.
Composer *composer.Registry
// Components advertises, per pluggable seam (identity, secret_store,
// recording, policy_engine, sandbox, ...), the SELECTED running implementation
// and the recommended production default, for honest /healthz visibility. Nil
// => the components object is omitted.
Components map[string]ComponentInfo
// AgeKeyDurable reports whether the secret store's age key was SUPPLIED
// (WARDYN_AGE_KEY/-age-key non-empty) vs ephemerally generated at boot. When
// false, stored secrets are unreadable after a restart — surfaced by
// /setup/status as a durability warning. Computed at boot in cmd/wardynd.
AgeKeyDurable bool
// LocalLoopback reports whether the HTTP listen address binds only loopback.
// It feeds SetupAuth.LocalLoopback so the wizard can explain the local-mode
// posture. Computed at boot in cmd/wardynd (listenIsLoopback).
LocalLoopback bool
// LocalTrustForwarder, when true, tells the LocalMode no-auth bypass to accept a
// NON-loopback request peer (r.RemoteAddr). It exists for the compose/team
// deployment ONLY: there wardynd binds 0.0.0.0 inside a container but the host
// publishes the port loopback-only (127.0.0.1:PORT), so a host UI/CLI request
// arrives at wardynd from the docker bridge gateway, not loopback. The LAN
// protection in that topology is the loopback PUBLISH (a LAN peer cannot reach a
// 127.0.0.1-bound host port at all), not the peer check — so the peer gate is a
// false positive there. The DNS-rebinding Host gate still applies. NEVER set this
// for a directly-bound host-mode wardynd on 0.0.0.0: that would re-open the LAN
// no-auth exposure the peer gate closes. Default false; set by compose only.
LocalTrustForwarder bool
// ComposerBackends is the BOOT-snapshot readiness of every configured composer
// backend (including disabled + needs-key ones the live registry can't show).
// Surfaced by /setup/status. Nil when the composer is unconfigured.
ComposerBackends []ComposerBackendReadiness
// ScanAIAdvisor, when non-nil, enables the ADVISORY AI workspace-scan fallback
// (internal/workspacescan/ai.go): after the deterministic DeriveProfile, when
// the profile is low-confidence or left unrecognized samples (ShouldAdvise),
// this gap-fills EMPTY fields only and can only RAISE NeedsReview — it never
// overrides a deterministic fact and FAILS OPEN (any error keeps the
// deterministic profile unchanged and the upload still succeeds). Nil (default)
// = feature OFF, byte-identical to the deterministic-only behavior. Production
// wires it (from WARDYN_SCAN_AI_ADVISOR) to a workspacescan.AdviseProfile
// closure; it doubles as the test seam so tests inject a fake instead of
// shelling out to a real coding-agent CLI.
ScanAIAdvisor func(context.Context, workspacescan.ScanFacts, workspacescan.WorkspaceProfile) workspacescan.WorkspaceProfile
}
Config holds the API server's non-secret configuration and injected collaborators. All interface fields except Runner are required; Runner may be nil for headless API-only operation (runs stay PENDING with a clear message).
type ImageBuilder ¶
type ImageBuilder interface {
// BuildDevcontainer builds the devcontainer for repoURL@ref and returns the
// local image reference to run. outputTag is the deterministic per-run tag
// the result is committed under.
BuildDevcontainer(ctx context.Context, repoURL, ref, outputTag string) (imageRef string, err error)
// BuildFromDevcontainerFiles builds an image from IN-MEMORY generated
// devcontainer files (relative path -> content, e.g.
// ".devcontainer/devcontainer.json") rather than a repo checkout, returning
// the local image reference. It drives the SAME hardened envbuilder path as
// BuildDevcontainer. Used for an onboarded workspace WITHOUT a wired
// devcontainer, where core A generates a minimal one from the detected
// profile (plan A5). outputTag is the deterministic profile-hash-keyed tag
// the result is committed under.
BuildFromDevcontainerFiles(ctx context.Context, files map[string]string, outputTag string) (imageRef string, err error)
// FinalizeBase wraps an arbitrary USER-supplied base image (Bring Your Own
// Image) with Wardyn's runner tools + a cleared ENTRYPOINT, returning the
// runnable local image reference. No untrusted build, no registry push — just
// the trusted FROM+COPY finalize stage; the base is pulled only if absent, so
// a host-pre-pulled private image works. outputTag is the per-run tag.
FinalizeBase(ctx context.Context, baseRef, outputTag string) (imageRef string, err error)
}
ImageBuilder builds a per-run sandbox image from a devcontainer repo. It is target-agnostic (the parity rule): the concrete envbuilder implementation is wired in wardynd behind the "docker" build tag, so the control-plane default build carries zero target-specific code. Nil disables devcontainer builds.
type MintBroker ¶
type MintBroker interface {
MintForGrant(ctx context.Context, caller *identity.Claims, grantID uuid.UUID) (broker.Minted, error)
RevokeRun(ctx context.Context, runID uuid.UUID) error
}
MintBroker is the credential-mint surface the API depends on (internal/broker).
type RecordTaskResult ¶
type RecordTaskResult struct {
RunID uuid.UUID `json:"run_id"`
// Label is the operator-chosen session name (e.g. "build & test"). Persisted
// because sessions are user-named, not derived — the session key is a slug of
// this, so the label carries the original display text.
Label string `json:"label,omitempty"`
Mode string `json:"mode"` // auto | interactive
// Confined distinguishes a VERIFY session (default-deny egress, limited to the
// workspace's approved set + baseline) from a learning session (open egress).
// Same interactive attach machinery; the flag flips AllowAllEgress and lets the
// UI list learning sessions on the Record step and verify sessions on Verify.
Confined bool `json:"confined,omitempty"`
// LLMMode + Model record the auth the session actually ran with (the operator's
// configured provider): subscription | api-key | none, plus the pinned model.
// Saved with the session so it's visible and a verify replays the SAME auth as
// the recording (the operator's setup, not a re-derived guess).
LLMMode string `json:"llm_mode,omitempty"`
Model string `json:"model,omitempty"`
Status string `json:"status"` // recording | recorded | record_failed
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at,omitempty"`
// Observations is the deterministic recordmode.Capture aggregate of what the
// run ACTUALLY used, computed server-side from its audit events at
// termination — never from a sandbox upload.
Observations *recordmode.Observations `json:"observations,omitempty"`
// SecretNamesMinted resolves Observations.MintedGrantIDs to the secret /
// grant names actually exercised, for the "proven used" checklist render.
SecretNamesMinted []string `json:"secret_names_minted,omitempty"`
// EgressPromoted marks that this task's observed hosts were merged into the
// workspace's ApprovedEgress (operator action, never automatic).
EgressPromoted bool `json:"egress_promoted,omitempty"`
// KernelSensorBlind: the run executed under CC3/Kata where the host eBPF
// sensor cannot see — proxy decisions were the sole egress signal.
KernelSensorBlind bool `json:"kernel_sensor_blind,omitempty"`
// FailureHint explains a record_failed in operator terms (e.g. the sandbox
// couldn't reach the control plane, so no evidence landed).
FailureHint string `json:"failure_hint,omitempty"`
Caveats []string `json:"caveats,omitempty"`
}
RecordTaskResult is one task's Record Mode state, persisted opaquely in workspaces.record_results (map taskKey → RecordTaskResult). The api layer owns this shape; the store never interprets it.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the control-plane HTTP server. It is safe for concurrent use.
func (*Server) ReconcileOnBoot ¶
ReconcileOnBoot re-derives the state of every run left non-terminal by a PREVIOUS wardynd process (a crash, OOM, deploy, or Ctrl-C) so it is not stranded RUNNING forever with a live sandbox and un-revoked credentials (C3). The per-run completion watcher is an in-process goroutine that does not survive a restart; this rebuilds the safety net at boot. Best-effort: errors are logged, never fatal. Re-attached watchers run on the daemon base context so they outlive this call. Status (unlike Wait) reconstructs from the container's run-id label, so it works even though the previous process's in-memory exec state is gone.
type SetupAgeKey ¶
type SetupAgeKey struct {
Durable bool `json:"durable"`
}
SetupAgeKey reports whether the secret store survives a restart (a stable WARDYN_AGE_KEY was supplied vs an ephemeral generated one).
type SetupAuth ¶
SetupAuth is the active public-API auth mode: local (loopback bypass) | sso (OIDC) | token (admin bearer) | disabled (no auth configured, API closed).
type SetupBedrock ¶
type SetupBedrock struct {
Region string `json:"region,omitempty"`
Model string `json:"model,omitempty"`
// The three credential SOURCES resolveBedrockAuth accepts, in its precedence
// order (bearer > ~/.aws mount > resident SigV4). ANY one is sufficient — a
// mount- or bearer-configured host has NO aws-access-key-id/-secret secrets
// yet is fully ready, so gating readiness on CredsPresent alone wrongly reads
// "needs setup".
CredsPresent bool `json:"creds_present"` // resident aws-access-key-id + aws-secret-access-key secrets
AWSMount bool `json:"aws_mount"` // host-mode read-only ~/.aws bind-mount (SSO auto-refreshes)
BearerPresent bool `json:"bearer_present"` // bedrock-api-key bearer token secret (never resident)
// Ready is the server-computed readiness (region+model+any credential source),
// echoed so the UI doesn't re-derive — and drift from — this gate.
Ready bool `json:"ready"`
}
SetupBedrock is the Amazon Bedrock Anthropic-transport readiness snapshot.
type SetupCheck ¶
type SetupCheck struct {
ID string `json:"id"`
Label string `json:"label"`
Status string `json:"status"`
Platform string `json:"platform,omitempty"`
Detail string `json:"detail,omitempty"`
Fix string `json:"fix,omitempty"`
}
SetupCheck is one environment/readiness row. Status is ok|warn|fail|info; "info" is a permanent, non-fixable condition (e.g. no /dev/kvm on macOS) that must render as informational, not as a clearable warning. Platform lets the UI show environment-appropriate copy (linux|darwin|windows|wsl|any).
type SetupComposer ¶
type SetupComposer struct {
Enabled bool `json:"enabled"`
Default string `json:"default,omitempty"`
Backends []ComposerBackendReadiness `json:"backends"`
}
SetupComposer is the composer enablement plus each configured backend's readiness (a BOOT snapshot, so it can surface disabled + needs-key states the live registry alone can't show).
type SetupDeployment ¶
type SetupDeployment struct {
HostLike bool `json:"host_like"`
}
SetupDeployment reports whether the wardynd process itself sees a resident Claude login — true in host mode (run-host.sh: wardynd runs as the operator, ~/.claude + the claude binary are on its own PATH/HOME), false in the compose path (distroless container blind to the host). HONEST framing like detectKVM: this is "does THIS process see a resident claude", not "is it literally run-host.sh" — a compose container with ~/.claude bind-mounted would also read host-like. The UI uses it to fork the getting-started guidance (laptop/local vs team/server) and to explain why the LLM-access check is or isn't green.
type SetupFix ¶
type SetupFix struct {
Action string `json:"action"` // "add_secret" | "scan_workspace" | "none"
SecretName string `json:"secret_name,omitempty"`
WorkspaceID string `json:"workspace_id,omitempty"` // ID, not source — api.scanWorkspace takes an id
}
SetupFix is the structured, actionable remedy a UI button drives directly — no prose-parsing. Action "none" means informational only: there is no button, because the only fix is the OPERATOR widening their own ceiling (e.g. a dropped egress domain).
ponytail: v1 verifies PRESENCE only (Decision 3: declared-present, never "verified" in the UI copy) — a stored secret, an onboarded+scanned workspace, a surviving grant. Live credential verification (does the key actually authenticate?) is a FUTURE seam at the egress proxy (broker-verified calls), not here — this stays a fast, pure read of already-stored state, no live probe and no new injection surface.
type SetupHarness ¶ added in v0.2.0
type SetupHarness struct {
Provider string `json:"provider"` // "anthropic"
Captured bool `json:"captured"` // a token blob is stored
CapturedAt string `json:"captured_at,omitempty"` // RFC3339, when pasted
Aging bool `json:"aging,omitempty"` // captured longer ago than harnessTokenAging
SourceRunID string `json:"source_run_id,omitempty"`
}
SetupHarness is a Wardyn-managed subscription credential's readiness. Derived purely from the stored blob (presence + capture age) — PRESENCE only, honesty law: no green badge implies the token was live-verified. setup-token tokens live ~1yr with no machine-readable expiry, so Aging is a conservative age-based "reconnect soon" flag, never a hard expiry claim.
type SetupItem ¶
type SetupItem struct {
Kind string `json:"kind"` // "llm_access" | "secret" | "workspace" | "workspace_secret" | "repo_credential" | "egress" | "backend" | "config_pair"
ID string `json:"id"` // stable "<kind>:<key>", e.g. "secret:anthropic-api-key"
Label string `json:"label"`
RequiredBy string `json:"required_by"`
Status string `json:"status"` // "satisfied" | "missing" | "unverified"
Detail string `json:"detail,omitempty"`
Fix *SetupFix `json:"fix,omitempty"`
// Residency names WHERE the credential this item concerns actually lives at
// run time, derived from the FINAL spec's own delivery mechanism (never
// guessed): "proxy_injected" (an api_key grant — the value never leaves the
// wardyn-proxy sidecar, injection.go), "resident_mount" (a host credential
// bind-mounted into the sandbox, e.g. the Claude subscription mount), or
// "brokered_mint" (a github_token/git_pat grant — the broker mints/resolves
// a value and hands it directly to the in-sandbox git-credential helper at
// task time, internal.go handleInternalMint). Empty when not applicable
// (workspace/egress/backend rows carry no single credential).
Residency string `json:"residency,omitempty"`
}
SetupItem is a per-requirement readiness verdict for a composed run, computed DETERMINISTICALLY from the FINAL post-clamp spec (never LLM self-assessment) — the same trust rule composer.Grade uses (risk.go:72). It generalizes the composeLLMAccess pattern (compose.go:121-127) to every setup requirement a proposal implies: secrets, onboarded workspaces, repo credentials, egress.
ponytail: NOT SetupCheck (setup.go:72) — that type's Fix is free-text prose for a human to read; SetupItem's Fix is a structured action a UI button can drive directly (add_secret/scan_workspace), so it needs its own shape rather than reusing SetupCheck's.
type SetupPlatform ¶
type SetupPlatform struct {
OS string `json:"os"`
WSL bool `json:"wsl"`
// KVM: the host exposes /dev/kvm — lets the UI split Vault's "incompatible
// with this hardware" from a fixable "needs setup" (additive; old UIs ignore).
KVM bool `json:"kvm"`
}
SetupPlatform is the wardynd host's OS + WSL posture.
type SetupProvider ¶
type SetupProvider struct {
Tool string `json:"tool"`
Installed bool `json:"installed"`
LoggedIn bool `json:"logged_in"`
LoginDetectedVia string `json:"login_detected_via,omitempty"`
// AuthMode is how the CLI authenticates, when detectable: "subscription" (a
// resident Claude OAuth token is present — fresh OR expired; freshness lives in
// the llm_provider check Detail, not here) or "" (unknown; never guessed). The
// "api_key" value is reserved in the contract but not inferred for a CLI (no
// cheap honest signal); codex stays "" (no auth-file parse).
AuthMode string `json:"auth_mode,omitempty"`
}
SetupProvider is a resident coding-agent CLI (claude|codex) detected on PATH. LoggedIn is ADVISORY (a home-dir credential-file heuristic, not a live check).
type SetupRunner ¶
type SetupRunner struct {
Driver string `json:"driver"`
ConfinementClasses []string `json:"confinement_classes"`
ConfinementSubstrates map[string]string `json:"confinement_substrates,omitempty"`
}
SetupRunner echoes the runner name and the live confinement classes/substrates.
type SetupSecrets ¶
SetupSecrets reports present secret NAMES (reserved names excluded) and a convenience bool for whether both GitHub App secrets are set.
type SetupStatus ¶
type SetupStatus struct {
// Ready is server-computed and CONSERVATIVE (false when the runner is nil),
// so the wizard opens rather than hiding a half-configured bootstrap.
Ready bool `json:"ready"`
// Checks is the single list of environment/readiness rows the UI renders.
Checks []SetupCheck `json:"checks"`
// Auth is the active public-API auth posture.
Auth SetupAuth `json:"auth"`
// Runner is the sandbox runner + the confinement classes actually live on
// this host (from Runner.Capabilities, same source as /healthz).
Runner SetupRunner `json:"runner"`
// Composer is the AI Run Composer enablement + per-backend readiness snapshot.
Composer SetupComposer `json:"composer"`
// Providers reports resident coding-agent CLIs detected on the wardynd host.
Providers []SetupProvider `json:"providers"`
// Secrets reports which known secrets are present (NAMES only, reserved
// names excluded) — never any value.
Secrets SetupSecrets `json:"secrets"`
// AgeKey reports whether the at-rest secret store survives a restart.
AgeKey SetupAgeKey `json:"age_key"`
// HasRuns drives the wizard's "launch your first run" done state.
HasRuns bool `json:"has_runs"`
// Platform is the OS + WSL posture the environment-step copy keys off.
Platform SetupPlatform `json:"platform"`
// HostProxy is the host-side proxy detection (env/shell/git/tool-config/OS)
// the Host Proxy Getting-Started step renders. Read-only detection; it
// never configures anything (the upstream-proxy plumbing is separate).
HostProxy setup.HostProxyDetection `json:"host_proxy"`
// SCM is the presence-only git-credential posture (gh CLI login, helper,
// plaintext stores) the ScmProviderStep's ladder recommendations key off.
SCM setup.SCMPosture `json:"scm"`
// Bedrock is the AWS Bedrock Anthropic-transport readiness the "Connect a
// model" step renders alongside the API-key/subscription rows. Region/Model
// are boot-time operator config (non-secret, safe to echo); the AWS
// credentials themselves are never echoed — CredsPresent is a bool derived
// from secret-name presence, same as every other secret in this contract.
Bedrock SetupBedrock `json:"bedrock"`
// Deployment reports whether wardynd itself sees a resident Claude login
// (host mode) or is blind to it (compose/container).
Deployment SetupDeployment `json:"deployment"`
// Harness reports per-provider Wardyn-managed subscription credentials
// captured via container login (setup-token), so the wizard can show a
// "connected / expiring / reconnect" row that works in compose mode where
// there is no resident host login. Empty when no managed credential exists.
Harness []SetupHarness `json:"harness,omitempty"`
}
SetupStatus is the aggregate readiness snapshot for GET /api/v1/setup/status.
Source Files
¶
- approvals.go
- artifact_redirect.go
- attach.go
- attach_ticket.go
- audit.go
- auditspool.go
- compose.go
- compose_assist.go
- compose_setup.go
- grants.go
- harnesscred.go
- helpers.go
- http.go
- injection.go
- inline_policy.go
- internal.go
- me.go
- mitmca.go
- policies.go
- policy.go
- profile.go
- reconcile.go
- record.go
- recording.go
- runs.go
- scanresult.go
- secrets.go
- server.go
- setup.go
- site_config.go
- ui.go
- verify_fix.go
- verifyresult.go
- workspace_refs.go
- workspace_run.go
- workspaces.go