VI Platform Services

Configuration

Business logic depends on values that are not logic: a credit limit, a provider’s address, an API key, a feature that is on in one place and off in another. Such values change on a different schedule from code, differ between environments, and are sometimes secret.

In Toa a component declares the configuration it needs, and the platform supplies it. The component does not read environment variables or files.

§A contract for configuration

A component declares its configuration as a schema, with defaults:

# orders/manifest.toa.yaml
configuration:
  schema:
    type: object
    properties:
      limit: { type: integer, minimum: 0 }
      currency: { type: string }
  defaults:
    limit: 5000
    currency: EUR

Operations read it from the context:

export async function transition (input: Input, entry: State, context: Context) {
  if (input.total > context.configuration.limit)
    return new Error('LIMIT_EXCEEDED')

  entry.total = input.total
}

The schema makes configuration a contract in the same sense as an operation’s input. A value that does not fit is refused before it reaches the component, so an operation never has to handle a malformed setting.

§Layers of values

A component’s configuration is resolved from layers, each overriding the one before it.

  1. Defaults in the manifest: what the component’s author considers sensible.
  2. The Context: what this application wants instead.
  3. The environment: what a particular deployment wants.
# context.toa.yaml
configuration:
  orders:
    limit: 10000
    limit@staging: 100

The component is unaware of the layering. It sees one validated object.

§Secrets

A value that must not be stored with the application’s sources is declared as a secret:

configuration:
  schema:
    properties:
      api-key: { type: string, format: secret }

The Context then refers to the secret by name instead of containing it, and the value itself is given to the deployment separately. Inside an operation, a secret is not a plain string. It has to be explicitly unwrapped to be used, and it prints as redacted if it is logged or serialized by accident.

The declaration carries the intent. A reader of the manifest knows which values are sensitive, and the platform treats them accordingly at every step.

§Changing values while running

Configuration can be changed without deploying. A new set of values is created for a component, validated against its schema, and delivered to the running processes, which use it from then on.

Each change is recorded as a new immutable object, together with the identity that made it. Nothing is edited in place, so the history of what a component was configured with, and by whom, is kept.

A component can also be reset. It then returns to its deployed defaults and continues to follow them through later deployments.

§Schema changes

When a component’s configuration schema changes, values recorded for the old schema may no longer fit. Each set of values is therefore tied to the schema it was validated against. After a deployment that changes the schema, a component starts from its deployed defaults and does not inherit values written for a different shape.

This removes a class of failure in which a value set long ago silently breaks a new release.

§What configuration is not

Configuration is for values that tune behavior and are the same for every replica of a component. Two things resemble it and belong elsewhere.

Infrastructure addresses — the database, the broker, the cache — are deployment concerns declared in the Context for the extensions and connectors that use them. A component never sees them.

Business data that users change — prices, catalogs, permissions — is entity state. It has its own operations, events, and history, and it is not configuration even when it changes rarely.

Search
Search the documentation