Skip to content

Publishing modules and providers

Publish private Terraform and OpenTofu modules and providers to your registry with the lbr CLI or a CI action — providers signed for you, with no GPG in your workflow.

Early access. Registry is rolling out behind an organization flag.

Longbridge Registry also hosts private Terraform and OpenTofu modules and providers — on the native registry protocol both terraform and tofu speak, under the same host, keys, and quota as your images. Publish with the lbr CLI or a CI action; consumers pull with terraform init / tofu init (see Consuming modules and providers).

Publishing is an ordinary read-write act on a registry key — the same credential you push images with. There is no separate publisher role.

Publish a module

lbr registry module publish packages a module directory into a tarball and uploads it. The module is addressed <registry>/<name>/<system> and the line is created on first publish — nothing to declare ahead of time.

lbr registry module publish ./modules/vpc \
  --registry acme-prod --name vpc --system aws --version 1.4.0

The system is a free-form token, conventionally the primary provider (aws, gcp); a provider-agnostic module can use any label. .git, .terraform, and .terraform.lock.hcl are left out of the tarball. Modules aren’t signed — the registry stores and serves the archive as-is.

Consume it with a module {} block; see Consuming modules and providers.

Publish a provider

The provider protocol makes signatures mandatory — and Longbridge is the signing authority, so you never hold a GPG key. Build your provider the way you already do (goreleaser build), then hand the build directory — the per-platform zips plus manifest.json, the output of goreleaser build minus its signs: block — to lbr.

lbr registry provider publish \
  --registry acme-prod --type widget --version 1.2.3 --dist dist

Longbridge signs it for you with your registry’s managed key and serves the full provider protocol, so you can delete the signs: block from your .goreleaser.yaml. Longbridge is the checksum authority: a SHA256SUMS or signature you upload in managed mode is ignored, with a notice. The provider is addressed <registry>/<type> and its line is created on first publish, which is also when the managed key is minted.

Consume it with a required_providers block; signature verification happens automatically on the consumer’s init.

Publish from CI

The longbridge/registry-publish@v1 action wraps lbr registry <provider|module> publish --oidc. At run time it trades your workflow’s OIDC token for a short-lived, read-write registry key — so there is no stored secret and no GPG in the workflow. Create a keyless OIDC access rule in readwrite mode that trusts your repository once, then:

name: release
on:
  push:
    tags: ['v*']

permissions:
  contents: read
  id-token: write          # required — lets the job mint an OIDC token

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: goreleaser/goreleaser-action@v6
        with:
          args: build --clean          # produces ./dist, no signing

      - uses: longbridge/registry-publish@v1
        with:
          kind: provider
          registry: acme-prod
          type: widget
          version: ${{ github.ref_name }}   # a v1.2.3 tag publishes as 1.2.3
          dist: dist

For a module, set kind: module with name, system, and dir. A readwrite rule is required — a pull rule fails cleanly. The signing key is minted and held server-side, so a CI credential can never mint, rotate, or revoke it: a compromised workflow can at worst publish within its rule’s scope.

Where to next