Skip to content

Infrastructure as code

Manage Weldall CLI settings, scopes, resources, machines, skills, and assignments from YAML.

All administrative features of the Weldall server can be managed through its user interface. The most important building blocks – scopes, resources, machine clients, administrator-managed skills, and assignments – can also be described in YAML files. This turns the configuration into infrastructure as code (IaC).

Even when using IaC, people with the appropriate permissions can continue to use the user interface to make changes or create new scopes, resources, and other objects. Manual changes to managed objects appear as drift in the next plan and are reset to the state described in YAML when the configuration is applied. If a managed object is manually deleted, Weldall plans to recreate it. This only affects objects created or imported through IaC. Everything else remains untouched.

  1. Prepare a machine client

Use the Weldall CLI to run IaC commands. It authenticates with a machine client that was created in Weldall and granted the weldall:iac scope. Give the CLI its client ID and key through environment variables:

Terminal window
export WELDALL_M2M_CLIENT_ID=platform-ci
export WELDALL_M2M_KID=ci-2026
export WELDALL_M2M_PRIVATE_JWK='<private P-256 JWK JSON>'
export WELDALL_M2M_PUBLIC_JWK='<public P-256 JWK JSON>'

These variables also decide what help shows: the IaC commands appear in weldall --help only while they are set, because a browser login never makes them usable.

  1. Initialize the workspace

    Terminal window
    mkdir platform-access && cd platform-access
    weldall init --name platform-access --issuer https://weldall.example.com

    This creates weldall.yml, weldall.lock.yml, and a weldall/ directory. Commit the manifest and lockfile together.

A workspace might look like this:

  • Directoryplatform-access/ - weldall.yml Root manifest - weldall.lock.yml Versioned server state and object mappings - weldall/ - access.yml Scopes and assignments - runtime.yml Resources and machine clients - imports/ Optional, used by weldall import - emailAssignment-alice.yml Generated import fragment
    • …

The root manifest generated by init contains the workspace metadata and includes every YAML file below weldall/:

weldall.yml
apiVersion: weldall.dev/v1
workspace:
name: platform-access
issuer: https://weldall.example.com
include:
- weldall/**/*.yml

The included files are fragments. They do not repeat apiVersion or workspace; they contain only one or more object sections such as scopes, resources, or emailAssignments. The CLI merges all fragments into one complete configuration. Your team can organize the files by application, ownership, environment, or any other structure that works for you.

weldall/access.yml
scopes:
expenses_read:
key: expenses:read
description: Read expenses
emailAssignments:
alice:
email: alice@example.com
scopes:
- expenses:read
groupAssignments:
finance:
provider: company-directory
groupId: finance-team
scopes:
- expenses:read

scope.expenses_read, emailAssignment.alice, and groupAssignment.finance are names that IaC uses only inside this workspace. The part after the dot comes from the corresponding YAML map key. These IaC-only names do not change the actual objects in Weldall: A scope is still identified there by values such as expenses:read, and a person by an email address such as alice@example.com.

The group provider must already exist or be declared in the same workspace. Disabled drafts may be referenced but grant no permissions. The CLI does not check whether the specified group ID actually exists.

Resources and machine clients use the same file format:

weldall/runtime.yml
resources:
expenses:
key: expenses
name: Expenses API
resourceIdentifier: https://expenses.example.com/api
authorizationServer: https://expenses.example.com
downstreamClientId: expenses-api
enabled: true
skillDiscoveryEnabled: false
requestPrefixes:
- https://expenses.example.com/api
scopes:
- expenses:read
machines:
reporter:
clientId: expense-reporter
name: Expense reporter
enabled: true
publicKeys:
ci-2026:
kty: EC
crv: P-256
x: IOqiZZjB6aPt6WuPSZw_sJxwdEueJ3AFo-AkI-c_sx0
y: 8oDde4-gGqCC4jGY_vwNBbaAw8l4Xti8cv6NRtoinjk
resources:
- expenses
scopes:
- expenses:read

Only public machine keys belong in the manifest.

Managed connectors can also be declared in YAML. The manifest carries only public policy; OAuth client secrets are entered in the administration UI and are never accepted by the CLI. envelopeProvider is immutable after creation. Google connectors list exact Google OAuth scopes. Atlassian Cloud connectors must use resource-level grants, at least one allowed Jira or Confluence product scope, and at least one allowed site cloudId.

connectors:
google:
key: google
name: Google Workspace
type: google
enabled: false
envelopeProvider: LOCAL_ENV
requiredScopes: []
provider:
clientId: google-client-id.apps.googleusercontent.com
allowedScopes:
- https://www.googleapis.com/auth/calendar.readonly
defaultScopes:
- https://www.googleapis.com/auth/calendar.readonly
company_jira:
key: company-jira
name: Company Jira
type: atlassian
enabled: false
envelopeProvider: OPENBAO
requiredScopes:
- jira:use
provider:
clientId: atlassian-client-id
grantType: resource
products:
- jira
allowedCloudIds:
- 8594f221-9797-5f78-1fa4-485e198d7cd0
allowedScopes:
- read:jira-work
- read:jira-user
defaultScopes:
- read:jira-work

Connector manifests can be planned and applied like any other object. Manual UI edits to managed connector policy appear as drift, while provider secrets remain outside the manifest.

The CLI welcome page logo can be managed as a singleton setting. An empty URL removes the company logo:

cli:
logoUrl: https://example.com/brand-logo.svg

Administrator-managed skills use inline Markdown and explicitly declare every configurable field:

skills:
review_expenses:
slug: expenses.review
title: Review expenses
content: |
# Review expenses
Check each expense against the policy.
requiredScopes:
- expenses:read
visibility: HIDDEN_IF_UNALLOWED
meta:
tags:
- finance
- review
owner: Finance Operations
appearance:
icon: file-text
gradientFrom: "#555BD6"
gradientTo: "#7773E5"
darkGradientFrom: "#2A2660"
darkGradientTo: "#403A86"
lastUpdatedAt: "2026-08-20"

meta.appearance.icon accepts canonical kebab-case names from the Lucide icon catalog. Gradient overrides are applied only when both colors in a light or dark pair are six-digit hex colors; otherwise Weldall uses the deterministic slug-based fallback.

The slug is the skill identity, so changing it replaces the skill. An IaC-managed skill remains editable in the admin UI; such edits are drift and the next apply restores the YAML state. Administrator-managed skills may override resource-published skills with the same canonical ID. Discovered skills and catalogs cannot be imported or managed.

IaC also supports these public declarations; the manifest schema lists required fields and defaults:

Section Identity Credential provisioning
sharedConnections Stable key; scoped service accounts only Admin → Connections
groupProviders Stable key; management-api-v1 adapter Admin → Group Providers
loginProviders Stable provider id (UUID); preserves the callback URL Admin → Login Providers

Create with enabled: false, apply, provision the credential in the UI, then explicitly enable and apply again. Secrets never enter YAML/state; provisioning/rotation does not activate an object, and disabling preserves its credential. Both connector and connection must be enabled. Disabled group/login providers grant no group access/start no logins.

Shared connections require an existing connector, an existing credential-manager user and a nonempty list of non-system use scopes (scopes may be declared in the same workspace). All connector/use scopes apply even to administrators and credential managers. Personal OAuth cannot be imported.

Plans check credential presence and known prerequisites, not upstream token validity; planning/import neither decrypt secrets nor contact OpenBao. Activation/credential operations retain provider checks and fail closed. The login issuer is immutable; credential-bound client IDs or directory endpoints need explicit UI reprovisioning, not silent credential reuse.

weldall:iac permits public login/group policy changes, not reading or provisioning secrets. Login activation and authority changes to active providers require approval of that specific plan:

Terminal window
weldall plan --json
# Review requiresAuthorityConfirmation actions and copy this plan's digest:
weldall up --yes --acknowledge-authority <digest>

--yes alone is insufficient; changed plans need new approval, also for API callers. IaC cannot disable/delete the last active login provider or reset installation; the confirmed UI recovery path is separate.

Terminal window
weldall import connector company-jira --as connector.company_jira
weldall import sharedConnection atlassian-bot --as sharedConnection.bot
weldall import groupProvider company-directory --as groupProvider.directory
weldall import loginProvider <existing-provider-uuid> --as loginProvider.sso

Import writes public configuration/bindings and preserves identity, callback, activation, credential manager and ciphertext; the next plan is a no-op. State move/unmanage also retain credentials. Public UI edits are drift; secret rotation is not, but can invalidate a saved plan.

Restore deleted objects disabled, reprovision credentials, then enable explicitly: IaC cannot recover deleted secrets. Unmanaged/foreign dependencies block connector/group-provider deletion. Local deletion does not revoke upstream tokens.

Back up the database, apply migrations through 0021_iac_secret_primitives and deploy the matching server with a regenerated Prisma client before upgrading clients. Stored credentials/encryption formats are retained. Old server binaries against the migrated schema are unsupported; rollback requires a compatible backup.

New sections/imports require discovery feature iac-ui-secrets-v1; legacy workflows still support older servers. Omitted sections differ from empty maps: retain e.g. groupProviders: {} to delete the last managed object. Incompatible partial snapshots fail before mutation instead of deleting unknown objects.

Only an unfinished sign-in started before this upgrade needs a new click on “Sign in”; existing sessions remain valid. Callbacks are bound to provider creation time and version, so recreating a UUID cannot revive an old attempt.

Command Purpose
weldall init --name … --issuer … Creates a workspace with a root manifest, lockfile, and fragment directory.
weldall validate Reads and validates the root manifest and all fragments locally without contacting the server.
weldall plan Compares the YAML configuration with the current server state without changing anything.
weldall up Shows a fresh plan, asks for confirmation, and then applies it.
weldall up --yes Applies the plan without prompting, for example in CI.
weldall import <kind> <identity> --as <iac-name> Claims an existing object and gives it a name for this IaC workspace.
weldall unmanage <iac-name> --yes Stops managing an object without deleting the object itself.
weldall state mv <old> <new> Changes the name IaC uses to manage the object; the Weldall object remains unchanged.
weldall state pull Rebuilds the lockfile mappings from the server state.

weldall plan --json prints the plan as machine-readable JSON. With weldall plan --detailed-exit-code, exit code 2 signals that changes are pending.

A matching manual object is never claimed automatically. Import it explicitly:

Terminal window
weldall import emailAssignment alice@example.com --as emailAssignment.alice
weldall import scope expenses:read --as scope.expenses_read
weldall import skill expenses.review --as skill.review_expenses

Import preserves the object, writes its current configuration to weldall/imports/, and updates the lockfile.

Removing a managed declaration makes the next apply delete that object. To keep the object but stop managing it, remove the declaration and run:

Terminal window
weldall unmanage scope.expenses_read --yes

You can change the name used only inside IaC without renaming or recreating the actual Weldall object. For example, this command renames scope.expenses_read to scope.expenses_view inside IaC; the expenses:read scope in Weldall remains the same:

Terminal window
weldall state mv scope.expenses_read scope.expenses_view

You can also recover the lockfile from the server state:

Terminal window
weldall state pull

The complete field reference is the manifest JSON Schema.