Skip to content
Français
Cloud medium

What feint proves, and what it does not

2 min de lecture

Read this page in French

An emulator that let you believe in what it does not check would be worse than useless. This page answers a single question, before you install anything: what can I reasonably test against feint? In short, the API contract, the Terraform lifecycle and your client's behaviour are testable; pricing, quotas, real capacity and IAM permissions are not.

What you will get

By the end of this page, you will decide whether a given test makes sense here, state what establishes each verdict rather than trusting a promise, and recognise the limit that can cost you money.

  • Read the matrix row by row, in your own vocabulary.
  • Tell apart a "yes" from a "yes, with a runtime".
  • Identify the most dangerous limit, the one that lets traffic escape.
  • Understand why no coverage percentage is published.

The verdict matrix

Each row carries its verdict and what establishes it. A "no" is not a gap waiting to be filled: it is an assumed decision, with a written reason and a date.

What you want to validateVerdictWhat establishes it, or the limit
Terraform or OpenTofu syntax, plan and lifecycleyesthe conformance suite: apply, empty second plan, destroy
A full workflow with scw, octl or exoyesthe same run drives all three clients against one emulator
Request and response shapes, field by fieldyes, with a stated ceilingevery answer is checked against the provider's own API description
That your client reads a field the real cloud returnsyes, where a recording existsrecorded shapes arbitrate; the blind spot is counted
Dependency order: what refuses to be deleted under whatyes, while the relation is synchronouseach suite's refusal assertions
That your code decodes a refusalyeseach suite demands refusals and fails when none happened
Cloud-init, an SSH login, a firewall that filterswith a runtimekeys on the firewall capability the host declares
Two VPCs that cannot reach each otherOVN onlykeys on the isolation capability, never on a mode name
A load balancer that really distributesOVN, and per packthe runtime must be able and the pack must hand over
That a machine claimed running exists on the hostOVN, per providera suite reads the API's claims then checks them with incus
Reaching a private address from the hostdepends on the modekeys on the private_from_host capability
A kernel-level test: sysctl, modules, the boot pathwith a VM runtimea container shares the host's kernel
Retry and backoff against a 429, a 5xx or an outageyes, on the operations you armfault injection, deterministic and off by default
That a wrong image or instance-type id is rejectedyes, on demand--strict-catalog refuses anything outside the catalogue you declare
Quotas, real capacity, pricingnothe catalogue is a measured whitelist
Authentication and IAM permissionsnoauthentication is accepted, never verified
A resource that stays provisioning for a whileyes, on demand--consistency eventual walks the states, one transition per read
Object Storage, and anything addressed as s3.<region>nonot emulated, and the traffic really leaves
Terraform against Exoscaleyes, from provider 0.71.0older versions are refused by user agent
Multi-zone or multi-region on Exoscalenoone zone per process, and the reason is the client
Traffic actually flowing through a gateway or NATnorecords move, packets do not

How to read a verdict

"yes" means a real client drove that operation in the reference run, and that the suite asserted something about the answer. It does not mean every field of every response is right: two defects sat behind a green axis for months, which is exactly why the project publishes seven independent axes and never adds them up.

"with a runtime" means the control plane answers without one, and the claim only holds when machines are real. The right way to know is to ask the emulator rather than guess:

Fenêtre de terminal
curl -s localhost:4599/_feint/health | jq .capabilities
{
"machines": false,
"addresses": false,
"firewall": false,
"isolation": false,
"own_kernel": false,
"balancing": false,
"private_from_host": false
}

A capability nobody declares reads as absent, deliberately: a check skips itself rather than asserting what no driver promised. What each mode delivers is detailed in Run real machines.

"no" is a decision with a reason, not an oversight. Several of these rows would change if somebody did the corresponding work, and those are open issues, not omissions.

The most important limit: a local run can still reach the real cloud

This is the only limit on this page that can cost you money. feint serves APIs locally; a client can compose the address of a product outside that perimeter itself, and its requests then leave for wherever they were going.

The measured case is Object Storage. On a real configuration, the same apply created its IP address on the emulator then carried on to s3.fr-par.scw.cloud, because the Terraform provider hardcodes that address for this product. Only a 403 on fake credentials made the operation harmless that day.

The five refusals, and their reasons

These five rows come up most often, and each is an explicit trade-off rather than work left undone.

Identifiers are not checked against anything, unless you ask. By default, a create naming an image or an instance type that does not exist succeeds: Scaleway answers 201 for an image UUID that exists nowhere, Outscale accepts ami-99999999. The real clouds refuse all three. That default is not an oversight: the emulator has no inventory, so the only identifiers it could recognise are the ones it invents, and a configuration that hardcodes a production identifier would then fail on the one thing that has nothing to do with what it is testing.

The cost was real, and 0.13.0 gives you a way to remove it where it hurts: --strict-catalog takes a JSON file where you declare, per provider, the images, templates and machine types your project allows.

{
"scaleway": {"images": ["ubuntu_jammy"], "types": ["DEV1-S"]},
"outscale": {"images": ["ami-fe1a7001"], "types": ["tinav6.c1r1p2"]},
"exoscale": {"templates": ["Linux Ubuntu 22.04 LTS 64-bit"], "types": ["standard.medium"]}
}

A create that falls outside it is then refused in each cloud's own error shape, not in a house format: your client renders its ordinary not-found message.

{"Errors":[{"Code":"5023","Details":"The ImageId 'ami-99999999' doesn't exist.","Type":"InvalidResource"}]}

Without the flag nothing moves: the compatibility mode is byte-identical, and the sentence above stays true word for word. A provider the file does not mention is not checked.

Authentication is accepted, never verified. No signature is checked, on any provider. Credentials merely have to be well formed, because the SDKs validate their shape client-side before sending anything. The consequence is direct: never expose feint on a network you do not control. It is a development tool that grants everything to everyone, by design.

The catalogue is a measured whitelist. Instance types, images and their attributes come from values recorded at the provider, not from a live inventory. Quotas, real capacity and pricing are therefore absent, and no quota-exhaustion test makes sense here.

Lifecycle transitions are immediate. A server goes from stopped to running within the call. The states clients check are preserved, such as the refusal to delete a server mid-transition that Terraform depends on. But a refusal that only exists during a transient state cannot happen: on a real Outscale account, a snapshot requested before a volume settles is refused, whereas here the volume is available at once. A script chaining the two succeeds locally and can fail on the real cloud.

Traffic does not cross the gateways. On Outscale, a gateway or NAT moves records, not packets. The configuration applies, re-plans empty and destroys; what does not exist is the network path behind it.

The version floor that protects you: Terraform and Exoscale

Terraform drives Exoscale from provider version 0.71.0 onwards, and older versions are refused. That is not a whim: until that version the published provider built two clients and only one honoured the EXOSCALE_API_ENDPOINT variable, so an apply or a destroy split between the emulator and a billed account.

It was not a theoretical worry. A feint down run in the example stack's directory sent five signed requests to Exoscale's servers. The emulator therefore refused that client for ten days, from 26 August to 5 September 2026, while the fix made its way upstream.

Since then the refusal has turned into a floor. A provider older than 0.71.0 is recognised by its user agent and gets a 400 naming the version to pin; from 0.71.0 onwards the apply goes through. Two practical consequences for anyone following the old procedure: feint up no longer stops at the doorstep, and the FEINT_EXOSCALE_ALLOW_TERRAFORM variable is gone, having nothing left to lift. The exo CLI was never affected and drives this provider end to end.

Why no percentage is published

This is a methodological choice, and it explains the shape of this whole page. A single number lets a weak claim ride on a strong one. Saying "94 % coverage" would mix operations whose behaviour is demonstrated with others that merely answer something well formed.

For 0.13.0, the project therefore publishes two numbers that do not add up: 395 operations mounted, of which 375 driven by a real client in the reference run. The remaining 20 state at their route why no official client reaches them. The difference between "mounted", "probed" and "driven" is developed in Measuring fidelity.

Key takeaways

  • The API contract and the lifecycle are testable; pricing, quotas and permissions are not.
  • A "no" is a dated decision, with a written reason, never an oversight.
  • A local run can reach the real cloud: Object Storage is the measured case.
  • Identifiers are not verified: a typo passes here and breaks in production.
  • Authentication is never checked, so the emulator never goes on an open network.
  • Transitions are immediate, so a refusal specific to a transient state cannot be reproduced.
  • No percentage is published, because a single number would hide the weak claims.

Next steps

Is this site useful to you?

Fewer than 1% of readers support this site.

I maintain more than 700 free guides, with no ads and no tracking. Any support, even a symbolic one, helps cover hosting and keeps these resources free. Thank you for the help.

The form does not show? Open Ko-fi in a new tab.

Subscribe and follow my DevSecOps work on LinkedIn