Repository navigation
v0.8.0
Fugaro v0.8.0
Install:
- Homebrew:
brew install dimipaun/tap/fugaro - Go:
go install github.com/dimipaun/fugaro/cmd/fugaro@v0.8.0 - Or download an archive below and verify it against
checksums.txt(see docs/release.md).
Highlights
- A project's limits and its allow-list are now the project's own data instead of each job's environment:
fugaro project policy publishwrites the ceiling, the prices and the repositories that may run (allow_repos),fugaro project showandfugaro project repos ls|add|rmread and edit them, and a run reads the policy before it starts — one it cannot read halts the run rather than falling back to the job's own ceiling, for an installation whose rules and marker are in place (see the operator steps below). The policy'sprovidersblock is carried but not applied yet, and an installation that publishes nothing behaves exactly as it does today. - The operator can now write down where Claude goes:
routing.claudein your own config takes an explicit list of Claude model IDs andvia: direct | openrouter | auto(an absent switch meansdirect), andfugaro project policy publishcopies it into the published policy, which is how a run receives it. Your config is its only definition site — a repository'sfugaro.yamlhas noroutingkey, and a project layer that writes one fails to load rather than half-applying it. A run resolves each named ID to the route's own ID (read by the pins, the price lookup and the recordedserving_model/priced_as),fugaro validateprints that resolution or says it is unresolved offline (and checks a routed pin with the same price, variant and alias rules the run applies), andfugaro doctorreports the same resolution; a Claude pin with no Anthropic key and no switch is a note fromvalidate, never a refusal, since only the cloud knows which keys exist. fugaro secrets set NAMEnow stores the project's copy by default for a name that has a project-wide meaning (a reserved secret such asanthropic-api-key, a provider key, a named Claude account), with--repofor a repository's own copy and--project-widestill accepted as a synonym; a repository copy still wins over the project's, andsecrets lsfollows the same default. A project-scope key mounts on the repository's job without an extra opt-in, andfugaro doctorgains asecret-scopecheck that lists which names are project-scope and warns when a repository copy shadows one.- A route may claim one exact
anthropic/<id>, which is the operator's opt-in to have that one Claude model served through the provider route — from your project config or from the project layer'sclaude_models, never from a repository'sfugaro.yaml. That stage needsagent.auth: api-key,budget.mode: enforce, the repository inside the route'sallow_data_toand a price for the claimed ID (your ownmodel_priceswhen the claim comes from the layer), and a routed Claude that reports no cache reads is charged at the full input rate. - A recipe can be
mode: artifact— a run whose deliverable is a file rather than a pull request: it declares itsoutputs, runs the optionalplanstage and then the repository's owncommands.rendercommand, ends withoutcome: artifactsand neither pushes a branch nor opens a pull request.fugaro budget previewprints that run's compute ceiling beside the model ceiling and says it is not capped — the compute is bounded bytimeouts.total, not bybudget.per_run_usd. The outputs are selected, capped, hashed and uploaded toruns/<slug>/<run>/artifacts/and recorded inresult.json; the optionalcommands.proberuns over the staged set before the upload and fails closed;fugaro run --attachuploads files in with the launch;fugaro artifacts ls|gettakes the files back, withruns lsanddiagnoseshowing them; the user guide isdocs/artifact-runs.md.
For operators
- Upgrade the CLI (
brew upgrade dimipaun/tap/fugaro, or this release's archive with its cosign check). - If your installation has a database, deploy the rules first:
fugaro init --firebase <firebase-project-id>in the installation root writes the rules a policy is read under and the marker that records them.fugaro project policy publishrefuses to publish without that marker, and an older CLI'sinit --firebaseis exactly what redeploys the older rules under an unchanged marker — so use a 0.8.0 binary for this step.fugaro doctorreports the two skews separately, and only one of them halts: a marker whose generation is below this Fugaro's leaves no run reading a published policy — every run keeps the ceiling from its own job's environment, silently, andpublishrefuses until the marker is brought up — while deployed rules older than the marker make the policy node unreadable, so every budget-on run halts. - Roll the runner out in this order, because neither
fugaro upgradenorfugaro image refreshwrites a job's environment and onlyfugaro init --repodoes: (a) build and publish each workflow's image from this release, (b)fugaro init --repoin each checkout to writeFUGARO_RTDB_URLandFUGARO_FIREBASE_API_KEYinto each job, (c)fugaro upgrade(its cloud step isfugaro image refresh) so the jobs point at the new image. A rollout that skips (b) leaves every run reading its ceiling from the job's environment and never seeing the policy, and no check reports that state —fugaro doctor'spolicy-vs-jobsreports the neighbouring one, jobs still on a runner image that predates this Fugaro, which is what skipping (c) leaves. Step 2 must come before (b): aninit --repoplanned from a local config with nobudget.rtdb_urlremoves the backend from the jobs that have it, and the plan says so — pass the installation's--budget-mode(and--firebase) again if you use them. - Then adopt the model, once per installation:
fugaro project policy publishwrites the policy (the ceiling, the prices andallow_repos) andfugaro project repos add|rmedits the allow-list afterwards. The first publish seedsallow_reposfrom your config'srepos:map and the shared config's repository list, and a later publish keeps the node's own list — so removing an entry is how you un-enroll a repository. A published policy with an emptyallow_reposadmits no repository at all, which is not the same as an installation that has published nothing:fugaro runmakes the same check before it creates an execution.
Nothing changes until step 4: an installation with no published policy spends and is capped exactly as it does today. The one change that lands earlier is step 2's: an installation whose local config has budget.rtdb_url now depends on Firebase and Cloud Storage at launch and at run start — every job carries the backend's address and key whatever the mode, the launch reads the policy and leaves a token object in the runs bucket, and each run reads it before it starts — so either service being down fails closed (the launch is refused, or the run halts) instead of running on the environment ceiling. --budget-mode off still stops only the cap session, and an installation that never configured a database keeps today's behaviour exactly.
Not in this release: project-centric phases 3 to 6 (shared project jobs, enrollment as data, the OAuth pool and the backing Git repository, including the monorepo-folder export), the Compute Engine/Cloud Batch execution backend (the code is inert and backend: cloud-batch is refused by the config), the desktop console, and checkout: clone — still not usable, exactly as 0.7.5's notes said: fugaro init --repo and fugaro image refresh refuse to deploy or move a clone workflow's job, and the flip belongs to the shared project jobs this release does not ship. Checks 44 to 46 — the price catalog and the two senior-reviewer checks — had not been run when this release was cut: they are run against the sandbox installation after the tag, and anything they find becomes a 0.8.0.x patch release, never a moved tag. Policy as data, the routing switch, project scope by default and artifact runs have no live check written yet, so they are not covered by one before the tag.
Changelog
- cli: the routing resolution, printed when readable and said unresolved when not (#441)
- cli: the secrets argv scan knows pflag's no-value flags, and routing joins the never-published check
- merge: place next's project-scope section above "The assumption"
- gitprov, runner, cli: refuse a remote the run's binding cannot read, and the negative rules marker (#443)
- cli:
fugaro artifacts ls|get, and the artifact display inruns lsanddiagnose(artifact-runs Task 7) (#438) - Next: the phase-1 review findings, fixed on the tree that has them (#440)
- runner: the probe —
commands.probeon the staged set, fail closed (artifact-runs Task 5) (#439) - pricing: the canonical-to-route ID map, one resolver for pins, prices and records (#422)
- attach: files in —
--attach, the task spec and the download (artifact runs Task 6) (#425) - doctor, upgrade and the docs: what project scope changes (plan Task 2.3) (#430)
- runner,runstore: an artifact run's outputs — select, sanitise, cap, hash, upload, record (artifact-runs Task 4) (#427)
- cli: record why a mode with no database launches instead of refusing
- cli: fix two doc comments in project_cmd.go
- cli: compare the printed commands whole, not by substring
- cli: find the secrets subcommand the way cobra does, not by searching for the word
- infra: the drift note is asked before the mode note, and says what it means
- backend: carry LaunchSpec.Env on Cloud Batch too, from one place
- runner: refuse a remote that names another repository of the same host
- projectpolicy: refuse a document that names one thing twice
- spec: a project-scope key mounts on the repository's job (#431)
- merge: record next's pflag promotion in the go.mod require baseline
- merge: adapt the console docs test to the plan's five-column phase table
- batch: the job definition, Launch and the socket (compute-backend Task 7) (#423)
- secrets: project scope is the default (D2) — plan Task 2.1 (#400)
- security follow-ups: policy-marker, skew-direction, missing-database and routing-comment fixes, plus the still-needed backup-399-code ports (#409)
- runner:
mode: artifactruns the optional plan stage and the render step (#418) - console: the Go/console boundary (desktop-console Task 0.3) (#420)
- reaper: the job-first reaper, the queue deadline and the teardown paths (compute-backend Task 5) (#410)
- config: routing.claude, an explicit ID list, absent means direct (#414)
- console: what the watch feed guarantees to a child process (plan Task 0.2) (#412)
- cli,recipe: budget preview's compute ceiling (artifact runs Task 8) (#419)
- cli+runner: task 1.6's policy surface, and the eight review findings on it (marked job refuses before the clone, publish keeps routing it does not model, --delete under the etag it validated, doctor'… (#402)
- recipe,config,runner,cli: the artifact format, and the two refusals that keep it safe alone (artifact runs Task 1) (#404)
- projectpolicy: an explicit anthropic/ price or allowed model is accepted (main's P4), only the glob forms are refused
- console: pin the JSON contract the app reads, field by field (#406)
- blobx: PutFile and GetFile — the bytes, streamed (artifact-runs Task 3) (#403)
- openrouter senior reviewer: the claim in the shared project layer (Task 7) (#397)
- docs, policy: Check 52, the probes phase 3 depends on, and two policy-read fixes (plan Task 3.0) (#386)
- logs: the Batch arm of the installation's sink and the
labels.job_uidfilter (compute-backend Task 4) (#396) - cli:
fugaro project— policy publish/show, project show, repos ls/add/rm (task 1.6, part A) (#390) - runner: a claimed Claude senior, end to end (Task 6 of the OpenRouter senior-reviewer plan) (#394)
- secrets: a project-wide copy covers the name in init's missing lists (Check 40 step 40i) (#395)
- openrouter senior reviewer: the claim in pricing and config (Tasks 4 and 5) (#387)
- cost: the VM price table, the machine type's quantities, the billing window (compute backend Task 3) (#389)
- runner, cli: the slug binding and allow_repos, with the policySince launch gate (#382)
- runner, cli: the project's policy is the ceiling, and a policy that cannot be read halts the run (#379)
- projectpolicy: the policy object and the per-child read rules (phase 1, tasks 1.1 + 1.2) (#372)
Base images
Published by the images workflow for this tag:
ghcr.io/dimipaun/fugaro-history:0.8.0andghcr.io/dimipaun/fugaro-history:0(the sweeper and history job's image)ghcr.io/dimipaun/fugaro-base:0.8.0andghcr.io/dimipaun/fugaro-base:0(one index for linux/amd64 and linux/arm64; per-architecture tags0.8.0-amd64and0.8.0-arm64)
The images workflow then pulls each one with no login and fails if it is not public or lacks linux/amd64 (and, for fugaro-base, linux/arm64); each image's digest is in that run's summary.
The Terraform modules, the schemas and the plugin are versioned by this same tag.