Skip to content

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 19 Oct 20:07
· 532 commits to main since this release
Immutable release. Only release title and notes can be modified.
fabfc7d

Fugaro v0.9.0

Install:

  • Homebrew: brew install dimipaun/tap/fugaro
  • Go: go install github.com/dimipaun/fugaro/cmd/fugaro@v0.9.0
  • Or download an archive below and verify it against checksums.txt (see docs/release.md).

Highlights

  • A run's budget token is sealed: the launcher writes it at tokens/<slug>/<run>/budget-token encrypted under a key generated for that one launch, which travels in the run's own environment and nowhere else, so a peer run of a shared job that reads the object gets ciphertext it cannot use.
  • A project runs its repositories on shared Cloud Run jobs: a profile names one provider and the Claude accounts it serves, fugaro init deploys one job and one job service account per (profile, provider, account), and a checkout: clone workflow launches onto them instead of onto a job of its own.
  • A project can name several Claude accounts (claude_accounts), so a person's own subscription serves the project's runs from its own job: each account's token is mounted only on that account's job, and a launch is admitted only for the repositories the account's published list covers.
  • fugaro runs stats --by user reports spend and jobs per person over a window, and fugaro runs ls and fugaro watch name who launched each run in a BY column.
  • The desktop console accepts this release's documents: job, profile, provider, account and launched_by no longer make it refuse a watch --json or runs ls --json document whole, so the Live screen draws the run rather than a skew screen — in a console built from this release, which is a separate download (an older console still shows the skew screen).

For operators

The order matters, steps 2 and 6 are one command split in two, and step 7 is step 3 again: an image built from this release refuses a policy node published by an older one, and an older image refuses this release's node, so the policy is republished before any image moves, and the shared jobs move last.

  1. Per machine — upgrade the CLI: brew upgrade dimipaun/tap/fugaro.
  2. Per repo checkout — run fugaro upgrade --local, so the plugin pin follows the CLI without a cloud call. Its cloud step (the base image's pin, the daily check job and each workflow's build) is step 6, and it is deliberately not run here.
  3. Per installation, in your own terminal, outside any repository checkout — run fugaro init --firebase <gcp-project> (in a checkout it would also pin this release's base image and move the shared jobs onto it before step 5). It deploys one job and one job service account per (profile, provider, account), replaces the launchers' bucket binding with the one that reaches tokens/ as well as runs/ (the old binding is replaced, never deleted) and writes each shared job account's read-only grant on tokens/. fugaro doctor names a launcher still holding the pre-0.9.0 runs/-only condition and says this init is the fix.
  4. Per repo checkout, in your own terminal — run fugaro init --repo .. It writes each job account's read-only grant on tokens/<slug>/, beside the repository's own job. fugaro doctor names a repository that has not been re-applied; until it is, that repository's runs with a budget token stop at bootstrap, reading the token as a missing object.
  5. Per installation — republish the policy, and read the list out first, because the publish that follows rebuilds it: run fugaro project policy show and keep what it prints, then fugaro project policy publish --delete, then fugaro project policy publish, then read the diff the publish prints and repair the list with fugaro project repos add <owner/name> and fugaro project repos rm <owner/name>. A repository you had added by hand is dropped, and one you had removed comes back while the config still names it. Runs on a shared job halt in between, which is the fail-safe direction.
  6. Per repo checkout — rebuild the images with fugaro upgrade (its cloud step) or fugaro image refresh. This release's base image is resolved at its -pinned tag, its cosign signature is verified, and the digest is read back through the installation's own remote repository and pinned in base_images — nothing is copied and nothing is pushed — then the daily image check job is pointed at it and each workflow's image is rebuilt on it. A 0.9.0 policy read by an older runner image is refused, and the fix is the image, not the policy.
  7. Per installation, in your own terminal, on a machine where step 6 ran — run fugaro init --firebase <gcp-project> again. The shared jobs run the installation's base image, and neither fugaro upgrade nor fugaro image refresh touches them: only this re-apply of the installation moves them onto the base image step 6 pinned (this machine's base_images). Until it runs, a shared job's runs stop at bootstrap, because the older runner refuses the policy step 5 published, and neither the launch check nor fugaro doctor says so, since both read the pin rather than the deployed job.

Then run fugaro project policy show again, per installation, and read it back: it prints the profiles and, for each account a profile names, the repositories its token may be used by.

Known limits

  • Managed projects stay disabled, exactly as in 0.8.6 and 0.8.7. The groundwork is in this release and out of reach: the second mark the managed read now requires beside the creation marker (the fugaro_managed=true label on the project itself), fugaro managed flag, the inventory-aware fugaro managed apply, and the bounded form every value it quotes from elsewhere is printed in. Every command that would create a managed project or act as its deployer refuses with a plain message before any cloud call — fugaro init --create-project --managed, fugaro init --managed, fugaro managed flag, fugaro managed apply and fugaro restore — so no managed apply runs at all, nothing writes the project label or the marker, and nothing skips the typed confirmation it would have skipped. What holds all of it off is the deployer's scoped role: the installation module's managed_deployer_scoped defaults to false and fugaro init never sets it, because the fmgd- fence its one conditioned binding carries is an IAM condition GCP does not accept as written (9 clauses joined by 17 logical operators where 12 are allowed; resource.name.contains(), which it does not support; and resource.name on Artifact Registry repositories, Cloud Run jobs, Cloud Scheduler jobs, IAM service accounts and API keys, where it is not supported). The deployer account, its create-and-read role and your impersonation grant are created as before, and the fence is still to be redesigned and measured against a live project. fugaro doctor reports the scoped role's absence as an info line, not a warning, and --strict does not fail on it.
  • The Batch backend is still disabled. fugaro init --provision-batch refuses with a plain message before any cloud call, as it has since 0.8.6: its reaper role names batch.jobs.cancel, which is not a permission Google has, and the reaper is still to be redesigned. This and managed projects are the two things 0.8.6's notes said would return in 0.9.0, and neither returns here.
  • Only checkout: clone workflows reach a shared job, and only on GitHub. A baked workflow keeps its per-repository job, and no shared job serves Bitbucket: fugaro init deploys none for a Bitbucket profile, so such a repository keeps its per-repository job. A shared job also carries less than a per-repository one — no FUGARO_CHECKOUT (a full clone rather than a blobless one, and no mise install), no owner-configured model provider, no local compute-price override, the default budget grace, and no repository-level workflows.<n>.secrets — all of which are listed under "Which runs work on a shared job today" in docs/project-layer.md.
  • A shared job's service account can read every repository's tokens/ objects. That reach is what the seal is for: what it reads there is ciphertext whose key it does not hold. The installation-wide reach and the reasoning are stated in full in SECURITY.md.
  • No per-account budget dimension. The budget is keyed by model, repository, project and person, never by credential: an OAuth run adds to the notional (subscription, unbilled) counters, and a named account's own limits are Anthropic's, not Fugaro's.
  • fugaro project export is not in this release. The project-centric plan's phase 6 (a project's record written into a repository or a folder of one) is still to come, as are the project-level phases that would let a project be created and managed from a repository rather than from an installation. The credentials service, and the mobile and voice work, are not part of this release either.
  • The live checks have not been run, and only you can run them. These are owner-run, sandbox-only, and every one of them is still NOT RUN: Check 60 of docs/gcp-live-checklist.md, the sealed budget token end to end (a sealed run works, the tokens/ object is ciphertext, and the widened launcher condition and the job's read-only grant are read off the live policy); Check 53, a shared project job end to end (reserved by the project-centric plan's Task 3.7, its section not yet written into the checklist); the named-account path, for which no live check has yet proved an account-gated shared-job run end to end; and the managed checks (67a, 67b and 68, reserved in the managed-projects plan) — which cannot run in this release at all, since every managed command refuses, so the project label, the inventory-aware apply and the applying identity remain unmeasured. Check 38, the clone workflows, passed on 2026-10-17 and its record is still to be written up, which is why checkout: clone ships here rather than earlier.

Changelog

  • website: the home page's prompt line, a Benefits section, and the diagram caption (#690)
  • website changelog: no false "not yet released", and a product summary per release (#681)
  • website: the caps page leads with what a job costs, from Fugaro's own runs (#688)
  • website: "The run, step by step" is one vertical timeline (#679)
  • website: the console's screenshot slider, data-driven and empty-safe (#686)
  • website: re-derive the budget figures after the 0.9.0 merge (#698)
  • website/getting-started: the command above the fold, then the steps a real installation takes, then the details (#684)
  • 0.9.0 review minors: deflake the stream-refusal test, symbol anchors in the JSON contract, and true LaunchedBy wording (#683)
  • mobile P4.4: the build-time app config, eas.json, and the internal-testing path (#675)
  • The mobile checks test's block helper gets its own name
  • console: the token layer (Task 6.1), and the owner's approval of Phase 6 recorded (#672)
  • 0.9.0: gate fugaro managed flag, admit the launcher grant replace on upgrade (#687)
  • 0.9.0: a missing token object must not stop every run at bootstrap (#682)
  • docs checklist: write Check 70b, the gist above the fold (P5, D187, RF23) (#627)
  • mobile P4.1: the CLI plan fugaro mailbox needs before it may be built (#581)
  • fix(cli): mint recency-dependent run ids from the clock, not a fixed date (#654)
  • website: the console's downloads are on, held to the release's own checksums (#657)
  • mobile/app.json: the EAS project id (expo.extra.eas.projectId), written by the owner's eas project:init (#676)
  • mobile P3.4: the audio interfaces, their fakes, and the pipeline over them (#592)
  • website: the phone's menu is its three labels, and the console joins the home call to action (#656)
  • fugaro.yaml: the workflow's total timeout is 3h and its stage timeout 2h30m (#669)
  • mobile P4.3: stage the shared package's test job for the owner to copy in (#582)
  • integration-0.9.0: the managed-return wording says 0.9.5, and 0.9.0 ships with the scoped deployer still gated off (#661)
  • Task: website: put the owner's real fugaro watch screenshot on the … (#655)

  • Phase 3 follow-ups: the shared-job launch reads the layer the run carries (and two items verified, one deliberately not implemented) (#596)
  • voice-module: the owner's spec, reconciled against the tree (docs only) (#636)
  • mobile: design push notifications (P5.2), docs only (#631)
  • console: the fixture scan stops reading the drawn clock as a removed value, and its 300 draws are seeded (#652)
  • fix(coordinator): cite the documents other works edit by content, not by line (#651)
  • managed: the project label (Task 3.0), written by the creating run and required by the read (#594)
  • console: the scrub's vocabularies come from the Go source, its clock shift is drawn, and the README says what the history still exposes (#644)
  • website: re-derive the byte budgets from the gates' own build, and correct seven claims the review left (#650)
  • fix main's red ci: the in-job-review pin's mobile claim and its brittle exact-set assertion (#649)
  • website: "Lambdas for coding agents", a chat with the agent, and one bold four-step diagram (#623)
  • Fix three review findings: managed state bucket, provider-key workflow secrets, accounts spelling check (#648)
  • Seal follow-ups B2: Check 60's commands, the job it names, the take comments, and project-job clone behaviour (#589)
  • mobile: P5.3's design — the cloud STT/TTS allow-list (design only) (#633)
  • Task: fix the flaky fake-gh PR-state tests (#599)

  • Task: integration-0.9.0 seal findings (doctor tells operators to dele… (#647)

  • console: accept runview.Row's and the live shapes' 0.9.0 keys (green console CI on integration-0.9.0) (#645)
  • rules follow-ups after P6.4: three untested action bounds, one wrong docs sentence (#646)
  • rules: mailbox P6.3 fields, device-node gate and pairing nodes (mobile P6.4) (#601)
  • shared/prompts: the prompt queue and the interrupt policy (P6.5) (#602)
  • mailbox: the phone's mailbox client, and the app's one write path (P3.6) (#604)
  • website: the nav's submenus open on hover on a pointer that hovers (#628)
  • mobile P3.2: the feed reader and the contract's document rules (#590)
  • console: the normal fixture, recorded from the real 0.8.6 CLI and hand-scrubbed (#643)
  • console: fetch the Electron runtime before any worker launches (the e2e ETXTBSY flake, test-only) (#639)
  • runs stats: spend and jobs per person, from the run records (#616)
  • Project-named OAuth tokens, task 3: the gates, doctor and SECURITY.md (#614)
  • plugin: the managed reference's header names the plugin's version
  • installation: scheduler actAs grant names its account literally; restore keeps the gcp_project line's quoting and comment (#613)
  • managed: bound every project, cloud and provider value the apply, flag and allow-delete print (#608)
  • watch: clip the BY cell to the column like runs ls does, and pin the clip, boundary and ls column in tests
  • cli tests: watch and runs ls agree on the launcher, live runs included
  • watch: show who started a run, in the text tables and in --json
  • infra review: one seam for the token mount, no silent job or mount overwrite
  • runs ls: the BY column moves the PR column off the end of the row
  • runview: carry who launched a run, sanitised, on the ls row
  • infra: one project job per Claude account, its token by static mount (stopgap)
  • managed flag review: refuse inside a Fugaro run, read the marker back, fail closed on an unreadable project number
  • cli: init's --managed warning names the flag for an existing project
  • skills: a managed project is the marker's, not only a creation's
  • managed flag: record a budget over a version-1 fact, and say so everywhere
  • skills lint: managed flag is an owner's command, and only a user-runs block
  • cli: pin the flag's default runs bucket, and the mutation-proved refusals
  • cli: fugaro managed flag, the owner's flag for an existing project (Task 6.1)
  • managed: WriteFlag, the marker an owner writes for an existing project
  • managed: fail the inventory closed on every state read, in bounded time
  • projectpolicy: a version-2 reader refuses this node, and the bump is what documents it
  • website: regenerate the security page from SECURITY.md's D4 text
  • projectpolicy: name the right direction in validProfiles' accounts comment
  • Task 3.6 review: keep the PR to the flip
  • cli: publish derives the accounts block, and show and the diff print it
  • projectpolicy: the accounts block's rules, with the tests that prove them
  • projectpolicy: the policy version goes 2 to 3
  • terraform test: pin the project job's tokens/ grant read-only, as the workflow job's is
  • doctor: a plain launcher's launcher-jobs line is its own reach, not a warning
  • review: pin the project job's grants, and harden doctor's launcher-jobs check
  • config: a profile may list Claude accounts, resolved at load
  • config: never reserve a platform name for a provider entry
  • cli: make the launcher check the profile's admission too
  • cli: refuse a launch on a bitbucket profile
  • code review of the branch: four fixes
  • backend: state the rule Execution.Run is actually held to
  • projectpolicy: refuse a profile name written twice
  • cli: cite the policy guard that exists
  • grants, docs and the project job's test: what a project job changed
  • doctor: report the launcher grants and the jobs a launcher can start
  • doctor: the launcher reach a migration changes (the red step)
  • watch: a run's job is a (profile, provider, account) name
  • managed: claim the installation's own resources, and say only what is true
  • integration: the seal's test and doc follow-ups on the merged tree
  • infra, projectpolicy: refuse a reserved name where a mount is decided
  • docs/git-providers.md: name providers.EnvGitHubAppID, not just the file
  • docs/git-providers.md: cite the plan task behind the shared job's App ID
  • docs, schema and the setup skill: checkout: clone is shipped
  • managed: read the whole-project inventory in managed apply
  • cli: prove the new refusals, and escape the derived conventional name
  • project jobs: give the shared job account the tokens/ read the seal needs
  • checkout: clone is shipped (cloneShipped), and the docs say what it needs
  • watch: the JSON's job, profile, provider and account (the red step)
  • managed: keep the run's confirmations where the inventory falls back
  • projects: the shared job carries the GitHub App's ID
  • infra: list and read the state bucket's state objects
  • config: reserve -api-key and codex-oauth-token, warn off convention
  • managed: read a Terraform state as the inventory's accounted side
  • budget token: the seal's launcher half (Slice B) (#523)
  • Revert "backend/batch: carry LaunchSpec.Env into the job's environment (again)"
  • Revert "infra/readiness: the drift note comes before the generic mode note (again)"
  • Revert "cli/project: refuse http2debug, and the delete's children get the same rules"
  • cli/project: refuse http2debug, and the delete's children get the same rules
  • infra/readiness: the drift note comes before the generic mode note (again)
  • backend/batch: carry LaunchSpec.Env into the job's environment (again)
  • runner: the marker refuses the key's other spellings, and one is registered
  • gitops: the budget marker never reaches git's environment
  • budget/token: one spelling of a key, the key's length, and the version byte
  • Revert "backend/batch: carry LaunchSpec.Env into the job's environment"
  • Revert "cli/project: four review fixes around the policy delete and the allow-list"
  • Revert "infra/readiness: the drift note comes before the generic mode note"
  • budget/token: make the ParseKey no-echo assertion real
  • infra/readiness: the drift note comes before the generic mode note
  • cli/project: four review fixes around the policy delete and the allow-list
  • backend/batch: carry LaunchSpec.Env into the job's environment
  • runner: register both spellings of the seal key with the redactor
  • budget token: the seal's runner half (Slice A)

Base images

Published by the images workflow for this tag:

  • ghcr.io/dimipaun/fugaro-history:0.9.0 and ghcr.io/dimipaun/fugaro-history:0 (the sweeper and history job's image)
  • ghcr.io/dimipaun/fugaro-base:0.9.0 and ghcr.io/dimipaun/fugaro-base:0 (one index for linux/amd64 and linux/arm64; per-architecture tags 0.9.0-amd64 and 0.9.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.