Skip to content

Milestones

List view

  • The runtime can be a machine you are not on. The protocol is the 0.12 contract over HTTP — the closed driver surface (#511), spoken by runtime-blind packs (#516), proven expressible by a pack that names no runtime at all (#517). It is never the Incus API, and the control plane on the far side is another repository: #195 drew that boundary when it replaced a cluster feature request with a sharper question, and this milestone is its implementation, not its renegotiation. What must hold: no Go type shared between the two repositories; no provider name on the wire; the single-host path unchanged — same tests, same suites, no new branch in any pack; a capability the remote plane does not declare counts as absent, and a check that needs it skips with the reason rather than asserting what nobody promised. This milestone has failed if: the boundary leaks (a shared package, a provider name, an Incus verb in the protocol); feint grows a scheduler, an IPAM or an authentication store for the remote plane; the single-host path pays anything; or "authenticated" is allowed to read as a security boundary — 0.16 already wrote the honest limit, and distance does not repeal it.

    No due date
    0/1 issues closed
  • A volume holds bytes, and the bytes survive the host rebooting. Losing them is made hard in the provider's own words: `protected` (Scaleway), `DeletionProtection` (Outscale) and `termination-protection` (Exoscale) are honored, and a refused quota answers in the dialect the real cloud uses — `quotas_exceeded` is a named error type in `scw/errors.go`, and `ReadQuotas` and `list-quotas` are surface the other two publish. Two facts gate this milestone: - **The 0.12 contract has no storage intent.** `plan.go` and `machine.go` name no volume and no disk: a feint volume today is bookkeeping with no bytes behind it. The first contract extension since 0.12 is the work, and the fourth provider (#517) is where it proves neutral. - **#196 is the decision this milestone takes.** "Does feint ever manage a resource whose loss would matter?" — answering yes implicitly, by shipping, is exactly what that issue warns against. The milestone does not open until the decision is written in `docs/limits.md`, and every guard it requires is an issue filed before the fact, not discovered after. Fidelity is not suspended in the homelab: every refusal an official client can see must be one the real cloud could send. Anything the controller refuses beyond the provider's own vocabulary lives outside the API path — `/_feint`, the exit code, the operations journal — and is documented as a divergence in `docs/limits.md`, with the personality named. This milestone has failed if: a "durable" volume loses bytes across a reboot; an official client receives an error shape no real cloud could produce; `feint clean` — a lab gesture — can sweep a protected volume; or protection is a feint invention instead of the three fields the SDKs already carry.

    No due date
    0/2 issues closed
  • The store stops being assumed true. On start and on demand, the controller observes what the runtime actually holds, converges it toward what was declared, and publishes only what it observed. "The state published is what the effect produced" is already the per-operation rule; this milestone makes it hold across a restart, and behind the operator's back. The defects that motivate it are measured: a machine stopped and started loses its routes to the peered subnets and nothing says so (#549); the reboot action restarts nothing and the API answers "running" (#547). Both are divergences between the record and reality that only observation can catch. Two rules are not negotiable, and both have history: - **Nothing that does not carry feint's own label is ever adopted, counted as drift, or deleted.** An audit obtained `network delete incusbr0` on the host's default bridge; `mustOwn` exists because of it. Adoption is an explicit, per-object act that writes the label before the store learns the name — never a sweep. - **An observation failure is not an absence.** A reader with two outcomes reports *error* as *not found* — measured here when a live account was reported empty for forty minutes. A reconciler with that defect converges by deleting the record, or the object; either way it destroys precisely when it is blind. This milestone has failed if: an unlabeled host object is ever named in a destructive command; a runtime error is read as "gone"; a reconcile undoes what a concurrent operation just did (the `Put`-resurrection lesson, this time between two loops); or the emulator personality starts reconciling — it must keep answering instantly from its store, because that is what an emulator is.

    No due date
    0/2 issues closed
  • A resource the API acknowledged outlives the process that acknowledged it. `terraform apply`, `kill -9`, restart, `terraform plan` — and the plan is empty; then `destroy`, another restart, and the resource is really gone, not resurrected. Today `docs/limits.md` says a restart starts empty, and it is honest. `Resource.Runtime` documents itself as "must survive a restart", and nothing makes that true except a manual `snapshot save` — a round-trip that already changes what it carries (#542: a stored number comes back a float64). This milestone makes the sentence true, and makes it true for exactly one personality. Two boundaries, both deliberate: - **`feint serve` keeps starting empty.** The emulator is fast, disposable and permissive, and every CI leg depends on a fresh one (`shapes --check` reads an emulator where only the catalogue answers — the #389 lesson). Persistence is the controller personality's promise. A flag that quietly gives the emulator a memory would hand a stale population to every gate that assumes a fresh one. - **What is restored is an input, not a fact.** The snapshot format is designed to travel between instances, and a crafted one has already produced `incus delete --force` on an operator's own instance. The persistent store loads through the same validation a request gets, or it does not load. Implementation is held to rule 6: `CGO_ENABLED=0` is enforced by the release build (`release.yml`, `Dockerfile`, `mise.toml`), so any SQL engine means either cgo or a transpiled dependency tree, and neither is justified before a measured need. The standard library writes an append-only journal and an atomic rename; the store's mutex is already the transaction, and the keyed map is already the uniqueness constraint. This milestone has failed if: a write the API acknowledged is missing after `kill -9`; a deleted resource comes back after a restart; a restored number has changed type; or the emulator personality pays for any of it — in startup time, in a file it did not ask for, or in a CI leg that stopped being fresh.

    No due date
    0/2 issues closed
  • Feint tells apart who is calling, in which scope, and why an operation is allowed or refused. Authentication compatibility, resource ownership and authorization are measured **independently** — and serving an IAM API never implies its policies are enforced. That last sentence is the whole milestone. This repository already knows the failure it names: `capabilities.balancing: true` was true about the runtime and said nothing about whether a pack delivered it, and a consumer following this project's own advice would have asserted a distribution nobody promised (#481). `CreatePolicy` served must never read as `CreatePolicy` enforced. **The order is deliberate, and it is the opposite of the tempting one.** Serving `CreatePolicy`, `CreateAPIKey` and `ListApplications` would buy API coverage in an afternoon and leave a policy that changes nothing about what `CreateServer` does. So: observe what the real clients send → a provider-neutral Principal → resources belong to a scope → authentication refusals, opt-in → one allowed and one denied path per provider → the evidence axes that publish it → the IAM APIs real clients actually demand Measurement first, generalisation second — the method that has served this project throughout. **Two boundaries that must be written before any code.** The core carries the pipeline and the decision; each pack keeps its own dialect, because a provider-neutral policy language would be feint inventing a cloud rather than emulating three. And permissive stays the default: a beginner running `terraform apply` against a local emulator must not pay for an authorization engine they did not ask for. Enforcement is something you turn on. **And the honest limit, stated in the documentation rather than discovered:** feint's authentication is a compatibility and authorization-testing mechanism, not a security boundary protecting the emulator from a hostile user on the same machine. Fake credentials remain fake. A milestone that let anyone believe otherwise would have shipped a false claim about safety, which is worse than shipping none. It comes after 0.13.0 because an authorization layer built over undifferentiated errors reports the wrong refusal in the right status, and after 0.15.0 because the machinery that makes a claim inspectable is what will make `authn` and `authz` readable rather than two more numbers.

    No due date
    0/1 issues closed
  • Three packs drove one runtime through three paths that had quietly diverged. Two of three had forgotten to hand their security groups to the host, and it survived for months because no test could see it (#475). This milestone replaces those three paths with one contract — and then proves the replacement cost nothing. The refactor is the means. The functional proof is the result. The promise: providers no longer drive the runtime directly. Every intent passes through a shared contract, with no loss of function — compute, network, firewall, load balancing, Terraform and OpenTofu, the official clients and the dataplane all keep working on the three providers. So the acceptance criterion is not "the new contract compiles". It is that **the contract can really express everything Scaleway, Outscale and Exoscale need**. A stack that passes at the API level while its traffic no longer flows proves the contract is incomplete, not that it is finished. The milestone therefore carries three kinds of issue, and they belong to one release. **Architecture** — the closed pack/runtime contract, GroupSync, Plan and Reconciler, the declared surface, runtime blindness, the recorder, the fake fourth provider, typed access to restored attributes. These prove the architecture is right. **Functional acceptance** — machines, networks, routing, public addresses, firewall, balancers, volumes, snapshots, images, cleanup, restore, concurrency. These prove the product works. An issue whose cause predates the refactor stays here if it says "what existed before does not really work under the new model". **Conformance and release quality** — the official clients, Terraform and OpenTofu, the third-party stacks, recordings and replay, shape, negative, behaviour, dataplane, generated docs, release preflight. These prove we know why we claim it works. One invariant of release, and it is the one this repository would otherwise be tempted to skip: **no axis of evidence regresses between 0.11.0 and 0.12.0 without a written explanation.** A fall is either a defect or a proof that was false — and the second is legitimate, so the number must never become an obligation that stops a bad proof being withdrawn. What leaves this milestone is only what is genuinely new and unrelated to qualifying the refactor: lab export, new orchestration, new UX flows, teaching scenarios, an API family no existing stack needs. The release this milestone earns is worth stating plainly: *three runtime paths that had diverged were replaced by one contract, and then the whole of feint — API, Terraform, real clients, real machines, networks, firewalls and balancers — was replayed to check the simplification was not paid for in fidelity.*

    No due date
    23/39 issues closed
  • The proofs feint already holds become directly usable by a person, by CI, and by the runtime. Nothing here adds an API: it makes what is measured legible, enforceable, and comparable between releases.

    No due date
    0/9 issues closed
  • A Feint environment stops being shell history. feint.yaml declares how the local cloud is brought up; feint up and down reproduce it; the same declaration runs locally and in CI without silently weakening its guarantees. feint exec scopes real clients to the environment, feint verify proves declared reachability, and controlled mutations let Terraform see drift it did not create. Terraform/OpenTofu remain the source of truth for infrastructure. Feint describes and verifies the lab around them; it does not become another IaC language.

    No due date
    0/9 issues closed
  • `negative` stood at 9% (34 of 355 operations) when this milestone was written. Measured on 2026-08-27 over the 371 operations of `coverage/evidence.json`, it stands at **66% (247)** — the 0.11.0 campaigns moved it, and the description said otherwise for a week. `shape` is the axis that now lags, at 60% (225). This milestone raises what a served route proves when things go wrong: injected failures rendered in each provider's own dialect, deterministic transient states a waiter can actually observe, and a catalogue that refuses a typo when asked to. It is also the direct prerequisite of identity. Before feint can say who may do what, it has to tell apart, in each provider's own words: 401 the credential is not valid 403 the caller is valid and forbidden 404 the object is absent — or deliberately hidden from this caller 409 the state refuses the transition 429 the caller is being throttled Those five are one shape today far more often than they should be, and an authorization layer built over them would report the wrong refusal in the right status. `0.16.0 — Identity is part of the request` waits on this.

    No due date
    1/13 issues closed