# Builds

The build turns a commit into an image ready to serve. This page describes how it is isolated, what the cache saves you, what it consumes from your tier, and the supply chain that scans, inventories and signs every image before it reaches production. It also says what to do when an image is refused for a vulnerability.

## What happens during a build

1. **Fetch** the commit, with a token limited to the repository and to reading for a connected repository (see [Git](git.md)).
2. **Detect** the framework and choose the stage: your `Dockerfile`, the composed image or the fallback (see [Frameworks](frameworks.md)).
3. **Check required variables**: the names declared in `.env.example` must exist on the environment.
4. **Install** dependencies, then **build** the image.
5. **Supply-chain gate**: scan, inventory, signature, then the move to `built`.

Each step is timestamped in the build log, which the **Déploiement** (deployment) screen streams live and `deployment_log_read` returns page by page. The log is kept up to 64 MiB without truncation.

## Isolation

- Detection reads files and runs no repository script.
- The image the platform composes interpolates no value from the repository into its instructions: commands come from a closed catalogue.
- The build runs unprivileged; a `Dockerfile` that asks for privileges is refused.
- The cache is never shared between two tenants, even with identical dependencies: a neighbour cannot slip an artefact into your image.
- A build is stopped after 30 minutes. Build workers also cap it at 4 vCPUs, 8 GB of memory and 20 GB of disk; the stop message names the limit reached and the value observed.

## The build cache

### The layer cache

A project's image layers are kept from one build to the next: a build whose dependencies have not changed starts from there. This cache is capped at 2 GB per project and kept for 30 days.

To build without any cache: `ignoreCache: true` in `deployment_create`, or **Reconstruire quand même** (rebuild anyway) on the **Déploiement** screen of a deployment that failed after the build.

### The Turborepo remote cache

A Turborepo monorepo can store and read its artefacts in the project's remote cache. In your CI or scripts, set:

- `TURBO_API` to the API address, `https://api.bastide.app`;
- `TURBO_TEAM` to the project identifier (`prj_...`);
- `TURBO_TOKEN` to a project token.

An artefact containing a secret is refused, and the refusal names the file.

In the console, open the project's **Réglages** (Settings), section « Crochets et cache » (hooks and cache), card « Cache distant Turborepo » (Turborepo remote cache): set the retention there (1 to 30 days, 7 by default) and purge the whole cache. An agent does the same with `build_cache_manage`, and can also purge specific hashes:

```json
{
  "projectId": "prj_01JQ8ZK4M7NP2RSTVWXYZ34567",
  "purge": ["a1b2c3d4e5f60718"],
  "retentionDays": 14
}
```

Without `purge`, only the retention changes; an empty `purge` list purges the whole cache. A purged artefact stops being served at once. The new retention applies to later uploads.

> **Note.** The « Cache de l'edge et images » (edge cache and images) panel on the **Domaines** (domains) screen is about the cache that serves your site and image transformation, not the build: see [Cache](cache.md).

## What a build consumes

| Tier | Build minutes per month | Concurrent builds | Image registry |
| --- | --- | --- | --- |
| Découverte | 100 | 1 | 10 GB |
| Standard | 500 | 2 | 10 GB |
| Pro | 2,000 | 5 | 40 GB |
| Souverain | 10,000 | negotiated | negotiated |

- **Minutes**: the quota is a hard cap, with no overage billing. A cancelled or skipped build counts nothing; a failure counts its seconds. The month is the one in which the build ends, in UTC. Once reached, it refuses further builds until the first day of the next month; live sites keep being served. A build in progress counts when it ends.
- **Concurrent builds**: beyond the tier's cap, a build waits its turn and loses nothing. The platform also has a global cap.
- **Registry**: every build says so beyond 80% of the quota; at 100%, the build is refused. Old releases are purged daily (the last ten of each environment and those that served production in the last 30 days are kept).
- **Image size**: beyond 5 GB, the build is refused; beyond 1 GB, it is reported.

The last closed month's consumption is shown in the project's **Réglages**, section « Consommation » (usage), or through `usage_read`. See [Limits](limits.md) and [Billing](billing.md).

## The supply chain

Between building the image and moving to `built`, the platform:

1. **scans** the image with Trivy;
2. **judges** its vulnerabilities against the catalogue of actively exploited vulnerabilities (copied from CISA's) and the register of exemptions;
3. **attaches** to the image, in the registry, the software bill of materials (SBOM) in CycloneDX format and the scan report;
4. **records** the scan in the audit log;
5. **refuses production** if the verdict requires it;
6. **signs** the image and those of its services. Nodes only run signed images.

The scan summary comes with the deployment's move to « Construit » (built): the number of high and medium vulnerabilities, and what production would refuse.

### What blocks production

| Vulnerability | Effect |
| --- | --- |
| Actively exploited (in the catalogue) | Production refused, no exemption possible. |
| Critical (CVSS 9.0 or more), with or without a fix | Production refused, unless an exemption is in force. |
| High | Not blocking, to fix within 7 days. |
| Medium | Not blocking, to fix within 30 days. |
| Low | Not blocking, to fix within 90 days. |

Criticality is read from the highest CVSS v3 score reported; the vendor's severity is only used when there is no score. In previews, the scan informs and does not block: that is where you find out what production would refuse. An image the platform could not scan, judge or sign is not promoted to production (`chaine_d_approvisionnement_indisponible`): retry the deployment, the cause is on the platform's side.

### Fix a refused image

The refusal (`image_vulnerabilite_critique` or `image_vulnerabilite_exploitee`) names up to five vulnerabilities, with the package, its version and the fixed version. The « Pourquoi il a échoué » (why it failed) card on the **Déploiement** screen shows it.

1. **Update the package** named to the fixed version, in your lockfile, then redeploy. If the package comes from the base image, rebuild on a newer base image.
2. **Remove from the final image what it does not need.** A flaw carried by a build tool (npm and its dependencies, a compiler) disappears if the final image does not contain it. With your own `Dockerfile`, build in several stages and finish on a runtime image without a package manager.
3. **Ask for an exemption** if no fix exists yet, or if it cannot be applied right away. An exemption only covers a critical vulnerability, never an exploited one. It is dated, justified, signed by the platform's technical lead at Glowsoft, and lasts seven days at most; it cannot be extended. Send the request to support, with the vulnerability identifier, the project and your workaround.

When an exemption expires, the next build is blocked again, and the refusal names the expired exemption and its date.

### Copyleft licences and secrets

- The « Bloquer les licences AGPL, SSPL et BUSL » (block AGPL, SSPL and BUSL licences) option (project **Réglages**, « Source et construction ») makes the build fail, in every environment, when a package under one of those licences is in the image. Without it, the package is only reported.
- An image variable whose value looks like a known secret refuses production (`image_variable_de_secret`). Revoke the value with its issuer first: it sits in an image layer, so it is published.

## Common errors

| Code | Remedy |
| --- | --- |
| `quota_de_construction_atteint` | Wait for the first of the month (UTC) or change tier. |
| `quota_gratuit_construction` | The free project has used its 100 minutes: wait for next month or move to a paid tier from **Facturation** (Billing). |
| `build_quota_registre` | Shrink the images or destroy unused previews; otherwise change tier. |
| `build_limite_duree` | The build exceeded 30 minutes: lighten the install, make use of the cache. |
| `build_limite_taille` | Image too large: multiple stages, a `.dockerignore`, fewer dependencies in the final image. |
| `image_vulnerabilite_critique` | See "Fix a refused image". |
| `image_licence_contaminante` | Replace the package, or turn the option off if the licence is accepted. |

See also: [Frameworks](frameworks.md), [Deploy](deploy.md), [Security](security.md), [Limits](limits.md), [Billing](billing.md).
