Starburst v2: context, modules, inspection and planning
Starburst embeds a pinned upstream Starlark interpreter with a Burst API. It does not implement a new parser. This increment adds starburst/v2; existing signed starburst/v1 policies still receive exactly their original five fields and cannot load modules. New signatures default to v2. Runtime versions are part of the signed artifact.
Authority and inputs
V2 preserves network_id, space_id, principal, action, and explicit now. It adds principal_kind, sorted groups, resource (id, classification, labels), and network (nodes, active_nodes, enrolled_nodes). Each node exposes ID, region, failure domain, available capacity, membership activity and capabilities. All nested input values are frozen.
The control plane resolves these facts inside its database transaction. User identities come from authenticated sessions; the internal repair actor is a service. Group IDs are scoped to the current space. Classification and labels are space-level settings inherited by objects; per-object classification is not implemented. Upload policy evaluation targets the space, while reads and repairs target the object ID. Membership activity means an unexpired, nonquarantined enrollment, not an independent health measurement. Regions, failure domains and capability labels are operator assertions, not hardware attestations.
A network operator may POST {"classification":"private","labels":{"team":"storage"}} to /v1/networks/NETWORK/spaces/SPACE/context, using the existing authenticated session, CSRF header and an Idempotency-Key of at least eight characters. This mutation is audited. Node-directory registration accepts capabilities, an array of up to eight identifiers. Policies may return required_capabilities; placement, repair and read rechecks enforce them. Like other storage obligations, capability requirements are pinned at publication and cannot vary with request context.
Context is bounded to 64 groups, 32 registered nodes and 16 labels. Oversized authority snapshots fail closed rather than silently omit facts. These bounds need revisiting before larger network deployments.
Reusable modules and signing
Imports resolve exclusively from an in-memory bundle or three embedded pure modules: burst/policy.star (decision constructor and predicates), burst/placement.star (node filtering and domain counting), and burst/automation.star (action constructors). A policy can use:
load("burst/policy.star", "decision", "classified")
def evaluate(ctx):
return decision(
classified(ctx, "private"),
required_capabilities=["encrypted_disk"],
reason_codes=["classification_checked"],
checks={"private": classified(ctx, "private")},
)The bundle contains entry source, custom modules, profile, declared planning capabilities and pinned embedded-module hashes. The existing artifact source digest covers the entire encoded bundle. Modifying a dependency invalidates the publication signature. Upgrading embedded helpers requires rebuilding and signing a bundle with their new hashes; mismatches fail closed. Custom modules cannot shadow burst/, access host paths or form cycles. Loaded module globals are frozen and share the entry program's execution budget.
# Each command uses operator-selected local files; the interpreter cannot read files.
dist/burstpolicy -key signer.pem -source policy.star -modules-dir ./modules \
-network n1 -space SPACE -principal PRINCIPAL -revision 2 > publication.json
# Explicit compatibility mode signs plain source using the original API.
dist/burstpolicy -key signer.pem -source legacy.star -runtime starburst/v1 \
-network n1 -space SPACE -principal PRINCIPAL -revision 3 > publication.jsonNo imports are exposed for raw legacy policies. V2 code importing embedded helpers should be bundled using the CLI so helper hashes are included.
Simulation and explanation
The build adds dist/starburst. Supply an absolute worker path and an input JSON document with explicit network, space, principal, action and time. Optional facts follows the exported Go Input/Facts schema. Local input is synthetic and never authorizes a real operation.
dist/starburst explain -source policy.star -modules-dir ./modules \
-input input.json -worker "$PWD/dist/burst-policy-worker"
dist/starburst simulate -source policy.star -input input.json \
-worker "$PWD/dist/burst-policy-worker"On non-Linux development hosts, add -development-soft-memory explicitly. Linux continues to enforce its worker limits even when that flag is supplied. Reports include runtime/profile, source and module hashes, canonical input hash, execution steps and the decision. reason_codes (up to 16) and boolean checks (up to 16) are policy-authored explanations; they are not independent proof that a condition is true. Worker errors stay sanitized and stdout/stderr remain bounded to 8 KiB each. Sources/bundles are capped at 64 KiB, module loading at 16 modules, and execution at 100,000 shared steps with existing process/deadline/memory limits.
Publishers can POST {"action":"read"} to /v1/networks/NETWORK/spaces/SPACE/policies/simulate to inspect the active policy for their own authenticated identity. Optional resource_id chooses the policy/grant target; storage existence, expiry, integrity and availability are not checked by this endpoint. Supplying source and optional runtime_version evaluates a candidate instead. Client-supplied facts or actor IDs are rejected. Responses identify authority_snapshot, grant results, obligation matching and the report. effective_allow is true only for an active unexpired policy with matching obligations, an allowing decision and a current grant; it is a policy/grant snapshot, not a promise that an object operation will succeed. Candidate evaluation never sets effective_allow. These read-only POST simulations require CSRF but do not create mutation outcomes or need idempotency keys. Normal operations still authorize again.
Bounded automation planning
Automation is a separate v2 profile with plan(ctx,event). It returns up to 16 proposals using repair(resource_id, reason_code) or notify(resource_id, reason_code). Both the bundle declarations and the host capability ceiling must allow each proposal. Every target must equal the event resource. No host repair, queue, network, notification, file or process effects are available.
load("burst/automation.star", "repair")
def plan(ctx, event):
return [repair(event["resource_id"], "replicas_missing")]dist/starburst plan -source repair.star -capabilities repair \
-input input.json -event object.degraded \
-worker "$PWD/dist/burst-policy-worker"
# Produce a portable candidate bundle for the server simulation endpoint.
dist/starburst bundle -source repair.star -capabilities repair > repair.bundleOperators may POST {"source":"ENCODED_BUNDLE","event":{"kind":"object.degraded","resource_id":"OBJECT"}} to /v1/networks/NETWORK/spaces/SPACE/automation/simulate. The server verifies the object belongs to that network and space, builds current authority facts and offers a repair/notify ceiling. The requested event is simulated; it does not assert that the object is actually degraded. object.expired is also accepted for simulation. Candidate automation bundles are not persisted or activated, and this endpoint does not verify a publication signature. execution_authorized is always false. A future executor needs a separate signed activation, authorization, audit, idempotency and retry design before it can act on proposals.
Verification and remaining work
Regression coverage includes legacy signed publication, module dependencies/hash pinning, cycle/path rejection, shared step limits, immutable facts, authority context changes, context spoof rejection, capability-constrained uploads and final reads, separate grant reporting, and automation capability/resource restrictions. Run sh scripts/check.sh for race tests and vet across all nine projects. Database schema version 3 adds classification, labels and node capabilities; PostgreSQL migration remains explicit. Live deployment, independent capability attestation, scheduled automation execution, editor tooling and a third-party module registry remain future work.