# Lanes

A lane is a tenant-owned execution environment. `production` always exists and cannot be retired. Customers may create other lanes for testing or staged integration work. A retired lane keeps its code as a tombstone, so an old queue message can never become valid again because someone reused the name.

Every tenant keeps its lanes in one registry. A lane has a code (lower-case letters, digits, `_` and `-`, up to 50 characters, starting with a letter or digit), a name and an order. The code and the production flag never change, and production cannot be deleted. New lanes are listed after the existing non-production lanes, production last. Creating a lane can copy another lane's entity types and bindings, so it starts on the same policy versions; it copies no entities, connections, webhooks, credentials or access.

```mermaid
flowchart LR
  subgraph FL["Fleetlane: one tenant, several lanes"]
    FLP["production lane"]
    FLT["test or staging lane"]
  end
  PAIR["Private integration API<br/>list lanes, validate, pair, workflow catalog"]
  subgraph AMON["Amon Sul tenant"]
    AP["production lane"]
    AX["non-production lane<br/>chosen when pairing"]
    BIND["lane bindings<br/>entity type to policy and version"]
    DEF["shared definitions<br/>published, versioned, immutable"]
  end
  FLP -->|"production pairs with production"| PAIR
  FLT -->|"test pairs with the chosen lane"| PAIR
  PAIR --> AP
  PAIR --> AX
  AP --> BIND
  AX --> BIND
  BIND -->|"pins a version"| DEF
```

Each lane points at its own bindings, and each binding names a published policy (or composite policy) and the version it runs. Two lanes can therefore run different versions of the same shared definition at the same time.

## Lane-owned and shared data

Entities, entity types, policy bindings, external systems, webhooks, machine API keys, managed integration connections, runtime locks, and operational delivery rows belong to one lane. Forms create these objects in the active lane; they do not offer a destination-lane selector. Editing an object cannot move it. A future copy-to-lane operation must create new primary keys and preserve explicit provenance.

Actions, sequences, policies, statuses, and event types are shared definitions. This lets the same published definition run in several lanes. A lane chooses its entities, policy bindings, credentials, external-system target, and webhook endpoints at execution time.

An executable `CALL` or `ODATA` action identifies an external system by stable `system_code` and supplies a relative path. It cannot contain an external-system UUID or a full URL. A complete executable version must supply both values; an incomplete draft may omit them. At run time, Amon Sul resolves exactly one active external system with that code in the run's lane. Missing or ambiguous resolution refuses the work. There is no production fallback.

## Trusted lane identity

The server decides which lane a request acts in; the client only asks. The browser sends the lane it is looking at and the platform checks it:

- A lane code that is not valid is refused (HTTP 400). A lane the tenant does not have is refused (HTTP 404). Neither is ever treated as production. After a refusal for a lane that was dropped, the browser returns to production on its own.
- A machine API key acts in the lane stored on the key, whatever the request asks for.
- Outside production, a person needs the `lanes.switch` permission and a role assignment in that lane. Without both, the request is refused (HTTP 403) and reads nothing from the lane.

Role assignments belong to a lane: a person's roles in `uat` are separate from their roles in production. When this model was introduced, every active assignment was copied into each existing non-production lane once, so nobody lost access; a lane created later starts with a copy of everyone's production roles, and after that its access is managed on its own. Every tenant role holds `lanes.switch`, and `admin` and `super_admin` also hold `lanes.manage` (create, order and retire lanes). A browser lane is a view and mutation context, not authority for machine requests.

Machine requests derive the lane and integration-association UUID from the API key that authenticated the request. Missing or invalid lane identity is refused. The key prefix must agree with its stored classification: production keys are live and nonproduction keys are test keys. A request body, action configuration, current connection, or historical action log cannot override authenticated provenance.

Managed integrations also carry an immutable association UUID. Entities, connection rows, webhooks, queue envelopes, and runtime records preserve it. A Fleetlane pairing slot (Amon Sul tenant, lane, Fleetlane tenant code) has exactly one association, and every re-pair of the slot reattaches it, so its entities and history are kept. Late work from a retired association remains invalid.

## Execution and delivery

Execution, policy routing and webhook receipt are separate stages. Keep the lane,
entity and run identifiers with your investigation. Read [execution limits](https://docs.amonsul.io/guides/execution-limits/)
before repeating work with external effects.

## Fleetlane pairing

A Fleetlane customer is one tenant with several lanes. Each Fleetlane lane pairs with one Amon Sul lane:

- A Fleetlane production lane pairs with the Amon Sul production lane.
- Any other Fleetlane lane pairs with an existing non-production Amon Sul lane that the Fleetlane administrator picks. The two products may name their lanes differently; the pairing is the mapping.
- Pairing never creates or guesses a lane, and it never pairs production with a non-production lane in either direction.

A lane pairs with one Fleetlane tenant, and a tenant owns one paired lane at a time. Re-pairing the same slot preserves its association and history while rotating credentials. Dropping the lane in Amon Sul is the way to release it. Ask the integration administrator to configure or change a pairing.

Fleetlane reaches Amon Sul through a private integration API that is not part of the public API: it lists lanes, validates and creates pairings, and reads the workflow catalog with the paired key. The workflow catalog is no longer a public route; use the [entity-type statuses route](https://docs.amonsul.io/developers/api/) to discover an entity type's tracks and statuses.

## Retirement

Production cannot be retired. Retiring another lane prevents new work and revokes
its credentials while retaining history labelled with its original context.
Coordinate retirement with the administrator and every connected application.
