Skip to main content
Statesman provides state storage and Terraform Cloud-compatible APIs for managing Terraform workspaces, runs, and state files. The UI uses Statesman’s internal endpoints, so you’ll need to enable webhook authentication and sync your organization and user data.

Prerequisites

Before you begin, ensure you have the following installed:
  • Go 1.25 or later
  • Atlas CLI (for database migrations)
  • SQLite (bundled with most systems) or S3 credentials (for production deployments)
If you don’t have Atlas installed, you can install it by running make atlas-install from the taco directory.

Installation

1

Navigate to the taco directory

From your project root, navigate to the Statesman directory:
cd taco
2

Run database migrations

If you’re using SQLite with persistence, create the data directory and apply the migrations before starting Statesman:
# Create the data directory if it doesn't exist
mkdir -p data

# Apply migrations
atlas migrate apply --dir file://migrations/sqlite --url "sqlite://data/taco.db"
If you encounter checksum errors, regenerate the hash first:
atlas migrate hash --dir file://migrations/sqlite
3

Build Statesman

Build the Statesman service:
make build-svc
This creates the statesman executable in your current directory.
4

Start the service

Run Statesman with your preferred configuration:
./statesman -storage memory -auth-disable
The service will start on port 8080 by default.
For local development, use -storage memory to avoid database setup. This option uses in-memory storage with SQLite as the query backend.

Configuration

Configure Statesman using environment variables:

Basic Configuration

OPENTACO_PORT=8080                    # Port to run the service (default: 8080)
OPENTACO_STORAGE=memory               # Storage backend: 'memory' or 's3'
OPENTACO_AUTH_DISABLE=true            # Skip OIDC authentication (for local development)

UI Integration

To enable the UI to communicate with Statesman, set the webhook secret:
OPENTACO_ENABLE_INTERNAL_ENDPOINTS=statesman-secret
This secret must match STATESMAN_BACKEND_WEBHOOK_SECRET in your UI’s .env.local file.

S3 Storage Backend

For production deployments, configure S3 storage:
OPENTACO_STORAGE=s3
OPENTACO_S3_BUCKET=my-bucket
OPENTACO_S3_REGION=us-east-1
OPENTACO_S3_PREFIX=opentaco/
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key

Additional Options

OPENTACO_SECRET_KEY=<secret>          # Secret key for signed URLs
OPENTACO_SQLITE_DB_PATH=<path>        # Custom SQLite database path

Using Statesman as a Terraform Backend

Once Statesman is running, you can configure Terraform to use it as a remote backend.

Terraform Configuration

Add the following backend configuration to your Terraform code:
terraform {
  backend "remote" {
    hostname     = "localhost:8080"
    organization = "your-org-name"

    workspaces {
      name = "my-workspace"
    }
  }
}
For production deployments, replace localhost:8080 with your Statesman instance’s public URL. If authentication is enabled, you’ll also need to configure credentials.

Authentication

If you’ve enabled authentication (not using -auth-disable), configure your Terraform credentials:
# Create credentials file at ~/.terraform.d/credentials.tfrc.json
{
  "credentials": {
    "localhost:8080": {
      "token": "your-auth-token"
    }
  }
}

Syncing Organizations and Users

The UI requires organization and user data to be synced to Statesman. Statesman resolves organizations by external_org_id (your WorkOS organization ID). If the organization cannot be resolved, /internal/api/units will return a 500 error.

Using the Sync Script

A helper script is available to simplify the sync process:
chmod +x cmd/statesman/sync-org.sh
./cmd/statesman/sync-org.sh
Edit the script with your WorkOS organization and user IDs before running it.

Manual Sync

Alternatively, you can sync manually using curl commands:
# Set your configuration
SECRET=$OPENTACO_ENABLE_INTERNAL_ENDPOINTS
ORG_ID=org_xxx                        # Your WorkOS organization ID
ORG_NAME=digger-org                   # Organization slug
ORG_DISPLAY="Digger Org"              # Organization display name
USER_ID=user_xxx                      # Your WorkOS user ID
USER_EMAIL=you@example.com            # Your email address

# Sync the organization
curl -X POST http://localhost:8080/internal/api/orgs/sync \
  -H "Authorization: Bearer $SECRET" \
  -H "X-User-ID: $USER_ID" \
  -H "X-Email: $USER_EMAIL" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "'"$ORG_NAME"'",
    "display_name": "'"$ORG_DISPLAY"'",
    "external_org_id": "'"$ORG_ID"'"
  }'

# Create the user in the organization
curl -X POST http://localhost:8080/internal/api/users \
  -H "Authorization: Bearer $SECRET" \
  -H "X-Org-ID: $ORG_ID" \
  -H "X-User-ID: $USER_ID" \
  -H "X-Email: $USER_EMAIL" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "'"$USER_ID"'",
    "email": "'"$USER_EMAIL"'"
  }'

Troubleshooting

Database Errors

“unable to open database file: no such file or directory” This error occurs when the data directory doesn’t exist. Create it first:
cd taco
mkdir -p data
atlas migrate apply --dir file://migrations/sqlite --url "sqlite://data/taco.db"
“no such table: organizations” This error indicates that migrations have not been applied. Run the migrations as described in Step 2 of the Installation section. Atlas checksum mismatch If you encounter checksum errors during migration, regenerate the hash:
atlas migrate hash --dir file://migrations/sqlite
Then retry the migration apply command.

Authentication Errors

403 Forbidden This typically indicates a webhook secret mismatch. Verify that OPENTACO_ENABLE_INTERNAL_ENDPOINTS matches STATESMAN_BACKEND_WEBHOOK_SECRET in your UI configuration.

Organization Resolution Errors

404 or 500 errors when resolving organization The organization has not been synced to Statesman. Run the sync script or manual sync commands described in the “Syncing Organizations and Users” section.

Storage Configuration

SQLite quirks By default, Statesman uses SQLite as the in-process query backend. No additional configuration is needed for local development. For PostgreSQL or MySQL backends, set TACO_QUERY_BACKEND and related environment variables (see docs/ce/state-management/query-backend for details).