Skip to content

Enterprise Deployment Repo

For enterprise customers, deploy ThinkWork from a customer-owned deployment repository, not from a fork of the ThinkWork source repository. The deployment repo pins a released ThinkWork manifest, stores customer-specific overlays, and uses GitHub Actions OIDC to deploy into the customer’s AWS account.

This is not a fork of the ThinkWork source repository.

This gives the delivery team a controlled place for stage configuration, customer eval packs, workspace defaults, skill packs, branding, approval gates, and deploy evidence while keeping upstream ThinkWork upgrades simple.

The first setup has one privileged bootstrap step. After that, every deploy runs through the generated customer repo workflow.

operator laptop -> bootstrap AWS/GitHub trust -> customer deployment repo
customer deployment repo -> GitHub Actions OIDC -> customer AWS account
release manifest -> customer S3/ECR/static hosting/AgentCore runtimes
customer overlays -> deployed tenant evals, skills, workspace defaults

Bootstrap creates or updates:

  • S3 bucket for Terraform state: <customer>-thinkwork-terraform-state
  • DynamoDB lock table: <customer>-thinkwork-terraform-locks
  • S3 bucket for release artifacts: <customer>-thinkwork-release-artifacts
  • GitHub Actions OIDC provider in the customer AWS account
  • One AWS deploy role per stage: thinkwork-<customer>-<stage>-deploy
  • GitHub Environments for each stage
  • GitHub Environment variables: AWS_REGION, AWS_ROLE_ARN, THINKWORK_ARTIFACT_BUCKET
  • Local deployment repo files, including thinkwork.lock, .github/workflows/deploy.yml, terraform/, and customer/

The CLI generates or prompts for required GitHub Environment secrets and writes them through gh secret set. Secret values are not written to the deployment repo or local ThinkWork metadata.

Secret nameUsed for
TF_VAR_DB_PASSWORDTerraform variable db_password
TF_VAR_API_AUTH_SECRETTerraform variable api_auth_secret

Run these commands from an operator machine that has temporary customer AWS administrator access and GitHub admin access to the deployment repository.

  1. Log in and verify readiness

    Terminal window
    thinkwork login

    The AWS identity should be in the customer AWS account. The GitHub identity should be authenticated with gh and allowed to create repository environments, variables, and secrets.

  2. Run the guided bootstrap

    Terminal window
    thinkwork deploy --bootstrap

    In an interactive terminal, this command walks through the setup: customer slug, stage, GitHub deployment repo, whether to create the repo if it does not exist, and production-like secret values. It creates or reuses a managed checkout, bootstraps dev and prod deployment authority, commits and pushes the generated repo files, dispatches the requested stage, waits for GitHub Actions, and prints the run URL plus discovered ThinkWork URLs.

    For scripted first deploys, pass the values explicitly:

    Terminal window
    thinkwork deploy --bootstrap \
    --customer acme \
    --repo acme-corp/acme-thinkwork-deploy \
    --create-repo \
    --stage dev

    With the example values above, the deployment repo is --repo acme-corp/acme-thinkwork-deploy.

  3. Deploy again after repo or overlay changes

    Terminal window
    cd acme-thinkwork-deploy
    thinkwork deploy

    From inside the generated deployment repo, bare thinkwork deploy reads the customer, stage, and repository context from the repo and dispatches the normal customer workflow. Use --customer acme --stage dev from outside the repo, or add --component overlays, --component smokes, or --no-wait when you need a narrower follow-up run.

  4. Remove a stage

    Terminal window
    thinkwork destroy

    From inside the generated deployment repo, this prompts for the stage, confirms the destructive action, dispatches the customer repo workflow with operation=destroy, waits for GitHub Actions, and reports the run URL. It removes the selected stage stack through Terraform while preserving the customer deployment repo, Terraform state bucket, artifact bucket, OIDC provider, and GitHub Environment configuration.

  5. Log in to the deployed stack

    Terminal window
    thinkwork login --stage dev
    thinkwork me --stage dev

The one-line path automates the manual bootstrap flow:

  1. Prompts for or resolves the customer, stage, repository, and managed checkout.
  2. Creates or clones the private customer deployment repo.
  3. Resolves the ThinkWork release manifest and computes its checksum.
  4. Bootstraps AWS state buckets, lock table, artifact bucket, OIDC provider, and per-stage deploy roles.
  5. Creates GitHub Environments and writes non-secret environment variables.
  6. Generates or prompts for required GitHub Environment secrets.
  7. Writes thinkwork.lock, workflow files, Terraform files, and customer/ overlay defaults.
  8. Commits and pushes the generated deployment repo.
  9. Dispatches .github/workflows/deploy.yml for the requested stage.
  10. Waits for the run by default, reports failed jobs on error, lists deploy evidence artifacts, and prints discovered Admin, API, AppSync, and Docs URLs when available.

Use the lower-level commands only when troubleshooting the one-line flow or when a scripted environment needs to split the steps apart.

  1. Confirm local tools and access

    Terminal window
    node --version
    npm --version
    gh auth status
    aws sts get-caller-identity
  2. Create or clone the customer deployment repository

    Terminal window
    CUSTOMER=acme
    REPO=acme-corp/acme-thinkwork-deploy
    gh repo create "$REPO" --private --clone
    cd "$(basename "$REPO")"
  3. Choose and verify the release to deploy

    Bootstrap defaults to the installed CLI version. For a repeatable manual deploy, pin the release manifest URL and checksum explicitly:

    Terminal window
    VERSION="$(thinkwork --version)"
    RELEASE="v${VERSION#v}"
    MANIFEST_URL="https://github.com/thinkwork-ai/thinkwork/releases/download/${RELEASE}/thinkwork-release.json"
    MANIFEST_SHA256="$(curl -fsSL "$MANIFEST_URL" | shasum -a 256 | awk '{print $1}')"
  4. Bootstrap with a dry run

    Terminal window
    AWS_REGION=us-east-1
    thinkwork enterprise bootstrap . \
    --customer "$CUSTOMER" \
    --repo "$REPO" \
    --stage dev \
    --stage prod \
    --region "$AWS_REGION" \
    --release-version "$RELEASE" \
    --manifest-url "$MANIFEST_URL" \
    --manifest-sha256 "$MANIFEST_SHA256" \
    --dry-run
  5. Run bootstrap for real

    Terminal window
    thinkwork enterprise bootstrap . \
    --customer "$CUSTOMER" \
    --repo "$REPO" \
    --stage dev \
    --stage prod \
    --region "$AWS_REGION" \
    --release-version "$RELEASE" \
    --manifest-url "$MANIFEST_URL" \
    --manifest-sha256 "$MANIFEST_SHA256" \
    --yes
  6. Set required GitHub Environment secrets if you bypassed top-level deploy

    Terminal window
    gh secret set TF_VAR_DB_PASSWORD --repo "$REPO" --env dev --body "$DEV_DB_PASSWORD"
    gh secret set TF_VAR_API_AUTH_SECRET --repo "$REPO" --env dev --body "$DEV_API_AUTH_SECRET"

    Repeat for prod before production.

  7. Verify GitHub Environment variables

    Bootstrap should have set these for every stage:

    Terminal window
    gh variable list --repo "$REPO" --env dev
    gh variable list --repo "$REPO" --env prod

    Confirm the values:

    VariableExpected value
    AWS_REGIONTarget AWS region, for example us-east-1
    AWS_ROLE_ARNarn:aws:iam::<account>:role/thinkwork-<customer>-dev-deploy
    THINKWORK_ARTIFACT_BUCKET<customer>-thinkwork-release-artifacts
  8. Review stage Terraform values

    Edit terraform/stages/dev.tfvars before first deploy. The generated file is intentionally minimal:

    stage = "dev"
    region = "us-east-1"
    account_id = "123456789012"
    database_engine = "aurora-serverless"
    lambda_artifact_bucket = "acme-thinkwork-release-artifacts"
    lambda_artifact_prefix = "releases/v0.12.5/lambdas"

    Add customer-specific non-secret values such as domain, hosted zone, Google OAuth client ID, callback URLs, SES settings, tags, or capacity settings. Do not put passwords, OAuth client secrets, API auth secrets, .env values, or connector secrets in terraform/stages/*.tfvars.

  9. Review the customer overlay contract

    The generated customer/deployment.json already contains each stage:

    {
    "schemaVersion": 1,
    "customerSlug": "acme",
    "stages": {
    "dev": {
    "tenantSlug": "acme-dev",
    "evalPacks": [],
    "seedPacks": [],
    "skillPacks": [],
    "workspaceDefaultPacks": [],
    "branding": null
    },
    "prod": {
    "tenantSlug": "acme",
    "evalPacks": [],
    "seedPacks": [],
    "skillPacks": [],
    "workspaceDefaultPacks": [],
    "branding": null
    }
    }
    }

    Add customer evals, skill packs, workspace defaults, seed packs, and branding under customer/ only when they are ready to apply.

  10. Commit any stage or overlay edits

    Terminal window
    git add terraform/stages customer
    git commit -m "chore: configure dev ThinkWork deployment"
    git push
  11. Dispatch the first dev deploy

    Terminal window
    gh workflow run deploy.yml \
    --repo "$REPO" \
    -f stage=dev \
    -f component=all \
    -f run_smokes=true

    Watch the run:

    Terminal window
    RUN_ID="$(gh run list --repo "$REPO" --workflow deploy.yml --limit 1 --json databaseId --jq '.[0].databaseId')"
    gh run watch "$RUN_ID" --repo "$REPO" --exit-status
  12. Download deploy evidence

    Terminal window
    mkdir -p "deploy-artifacts/dev-${RUN_ID}"
    gh run download "$RUN_ID" \
    --repo "$REPO" \
    --name "thinkwork-deploy-dev-${RUN_ID}" \
    --dir "deploy-artifacts/dev-${RUN_ID}"
    jq . "deploy-artifacts/dev-${RUN_ID}/deploy-summary.json"
    jq . "deploy-artifacts/dev-${RUN_ID}/smoke-summary.json"

    Keep the deploy summary artifact as customer deployment evidence.

  13. Log in to the deployed stack

    Terminal window
    thinkwork login --stage dev --region "$AWS_REGION"
    thinkwork me --stage dev --region "$AWS_REGION"

    Use the admin URL from deploy-summary.json to open the admin app and verify the first tenant, agent template, evals, and overlay files.

  14. Deploy production

    Before production, require approval on the GitHub prod Environment and set production secrets. Then dispatch the same workflow:

    Terminal window
    gh workflow run deploy.yml \
    --repo "$REPO" \
    -f stage=prod \
    -f component=all \
    -f run_smokes=true

The generated .github/workflows/deploy.yml accepts these inputs:

InputValuesUse
operationdeploy or destroyDeploys or destroys the selected stage stack.
stageAny stage generated by bootstrap, for example devSelects terraform/backend-<stage>.hcl, terraform/stages/<stage>.tfvars, and customer.deployment.json stage config.
componentallFirst deploy and normal release upgrades.
componentfoundationTerraform apply plus artifact/runtime/static sync, without overlays.
componentartifactsRe-copy release Lambda/static/runtime artifacts without Terraform apply.
componentoverlaysApply only customer evals, skills, workspace defaults, seed validation, and branding records.
componentsmokesRun smoke checks against an already-deployed stage.
run_smokestrue or falseRuns scripts/smoke.mjs after component=all or component=smokes.

For operation=deploy and component=all, the workflow runs this exact sequence:

  1. Check out the customer deployment repo.
  2. Read thinkwork.lock, customer/deployment.json, the stage backend file, and the stage tfvars file.
  3. Download thinkwork-release.json from the pinned GitHub release.
  4. Verify the manifest checksum when manifestSha256 is not CHANGE_ME.
  5. Assume the stage AWS role through GitHub Actions OIDC.
  6. Download release Lambda/static artifacts and upload Lambda zips into the customer artifact bucket.
  7. Run terraform init, select or create the stage workspace, and run terraform apply -auto-approve.
  8. Copy pinned runtime images from GHCR into the customer ECR repository.
  9. Update AgentCore runtimes to the copied stage image tags.
  10. Sync static site bundles from the pinned release.
  11. Run thinkwork enterprise overlay apply.
  12. Run smoke checks when run_smokes=true.
  13. Write and upload deploy evidence JSON artifacts.

For operation=destroy, the workflow verifies the repo and release metadata, assumes the stage deploy role through GitHub Actions OIDC, selects the existing Terraform workspace, and runs terraform destroy -auto-approve with the stage’s tfvars. It does not run overlays, smokes, runtime image copy, or URL discovery after the stack is removed.

Use this path when only customer/ files changed:

Terminal window
thinkwork enterprise overlay apply . \
--stage dev \
--region us-east-1 \
--dry-run \
--json
git add customer
git commit -m "chore: update acme ThinkWork overlays"
git push
thinkwork deploy --customer acme --stage dev --component overlays --no-run-smokes

CI writes overlay-report.json into the workflow artifact.

To adopt a new ThinkWork release, update thinkwork.lock in the customer repo, commit it, and run component=all.

Terminal window
VERSION="0.12.5"
RELEASE="v${VERSION#v}"
MANIFEST_URL="https://github.com/thinkwork-ai/thinkwork/releases/download/${RELEASE}/thinkwork-release.json"
MANIFEST_SHA256="$(curl -fsSL "$MANIFEST_URL" | shasum -a 256 | awk '{print $1}')"
tmp="$(mktemp)"
jq \
--arg release "$RELEASE" \
--arg manifestUrl "$MANIFEST_URL" \
--arg manifestSha256 "$MANIFEST_SHA256" \
--arg terraformModuleVersion "${RELEASE#v}" \
'.thinkwork.release = $release
| .thinkwork.manifestUrl = $manifestUrl
| .thinkwork.manifestSha256 = $manifestSha256
| .thinkwork.terraformModuleVersion = $terraformModuleVersion
| .artifacts.lambdaPrefix = ("releases/" + $release + "/lambdas")' \
thinkwork.lock > "$tmp"
mv "$tmp" thinkwork.lock
git add thinkwork.lock
git commit -m "chore: bump ThinkWork to ${RELEASE}"
git push
thinkwork deploy --customer acme --stage dev --component all

After dev passes, repeat the workflow dispatch for production.

PathOwnerPurpose
thinkwork.lockDelivery teamPins the ThinkWork release manifest URL and checksum.
.github/workflows/deploy.ymlThinkWork templateCI deploy path. Regenerated by bootstrap and updated on release bumps.
terraform/stages/*.tfvarsCustomer/deliveryNon-secret stage configuration.
terraform/backend-*.hclBootstrapS3 state backend for each stage.
customer/deployment.jsonCustomer/deliveryDeclares which overlay packs apply per stage.
customer/evals/Customer/deliveryCustomer eval packs.
customer/skills/Customer/deliveryWorkspace skill packs installed onto the target template.
customer/workspace-defaults/Customer/deliveryCustomer GUARDRAILS.md, MEMORY_GUIDE.md, and related defaults.
customer/seeds/Customer/deliveryCustomer-owned seed payloads validated by the overlay command.
customer/branding/Customer/deliveryStatic brand assets referenced by deployment config.
docs/runbook.mdCustomer/deliveryCustomer-local runbook generated from this deployment model.

Put reusable platform behavior, Terraform module changes, runtime fixes, schema migrations, built-in tools, and generic eval improvements in the ThinkWork source repository. Put customer-specific templates, guardrails, skills, datasets, branding, and stage configuration in the deployment repo.

That split is the whole point: customize without forking, then contribute general improvements back upstream through normal ThinkWork PRs.

SymptomLikely causeFix
manifestSha256 check failsthinkwork.lock URL and checksum do not describe the same release asset.Recompute the checksum from the manifest URL and recommit thinkwork.lock.
Workflow cannot assume roleGitHub Environment name, repository name, or OIDC trust policy does not match.Re-run thinkwork enterprise bootstrap ... --yes with the exact --repo and stage names.
Terraform backend init failsState bucket or lock table was not created, or the stage backend file points at the wrong region.Re-run bootstrap and inspect terraform/backend-<stage>.hcl.
Terraform prompts for variablesRequired GitHub Environment secrets are missing.Set TF_VAR_DB_PASSWORD and TF_VAR_API_AUTH_SECRET on the target GitHub Environment.
Overlay apply cannot find templatecustomer/deployment.json target template slug does not exist yet.Use the default generated template slug or create the template before applying overlays.
Smoke summary is degradedOne or more deployed URLs returned an error or could not be reached.Inspect smoke-summary.json, CloudWatch logs, and the workflow step logs.