Shared-services infrastructure
Add and change Tide AWS resources through Bento registered IaC.
Tide owns its Pulumi program in infra/. Bento owns the AWS authority, encrypted
state, recovery custody, GitHub environments, and reusable preview/deploy
workflows.
The registered production target is:
| Field | Value |
|---|---|
| Registration | tide-shared-services |
| AWS account | sharpe-shared-services (548277374575) |
| Region | us-east-2 |
| Pulumi project | tide-shared-services |
| Stack | production |
| Program directory | infra |
Never run pulumi up from a workstation. Never add static AWS credentials,
account IDs, role ARNs, or backend configuration to Tide's caller workflows.
Legacy distribution awaiting Bento adoption
The CLI release path still uses live resources that are not declared by the
current infra/ program or the tide-shared-services registration. This
inventory was verified read-only on 2026-07-26:
| Resource | Live identity | Observed state |
|---|---|---|
| Release origin | s3://tide-dist-sharpe in account 285688017134, us-east-2 | Versioning enabled; Project=tide, Component=dist-origin |
| Release edge | CloudFront E14YFZQ09AJUDI | Deployed and enabled; alias install.tide.run; origin tide-dist-sharpe.s3.us-east-2.amazonaws.com |
| Release publisher | arn:aws:iam::285688017134:role/TideReleaseRole | GitHub OIDC role used by .github/workflows/release.yml |
| Public DNS | install.tide.run | Live HTTPS consumer endpoint in front of the CloudFront distribution |
The release workflow also depends on repository secret
AWS_ROLE_TIDE_RELEASE and repository variable TIDE_CLOUDFRONT_DIST_ID.
Those values point at the legacy resources; they are not ownership evidence.
Adoption into Bento must preserve the bucket, distribution, public hostname, and publisher identity without replacement or interruption. The reviewed Pulumi plan must import their existing identities into a registered stack and show zero creates, replacements, and deletes before authority moves. Do not delete or recreate these resources as part of adoption.
Decide whether Bento must change
Read these files from Bento main before designing the resource:
docs/iac-project-registration.md, sections Resource authorization and Operating boundaries.iac/registrations/tide-shared-services.toml.iac/registrations/policies/tide-shared-services-preview.json.iac/registrations/policies/tide-shared-services-deploy.json.
A Tide-only change is permitted only when every refresh and mutation API action, resource ARN, account, and region already fits those exact policies and the registration. Do not infer authority from the AWS service name.
A Bento change is required before the Tide resource change when any of these is true:
- the Pulumi provider needs an AWS API action absent from either policy;
- the resource uses a new AWS service or a different ARN boundary;
- the account, region, Pulumi project, stack, working directory, owner, or cost allocation changes;
- a runtime needs new data-plane access;
- the resource needs an IAM role,
iam:PassRole, role chaining, or another centrally derived capability.
IAM mutation and other centrally refused actions cannot be enabled by widening Tide's policy. Stop and propose a Bento contract change instead.
Expand the Bento authority
Do this before opening or rerunning the Tide resource pull request. Always use a
new Bento worktree and branch created from current origin/main; never modify an
existing Bento worktree or stale feature branch.
bento_source=/absolute/path/to/an-existing-bento-clone
bento_change=/absolute/path/to/parent/bento-tide-resource-change
git -C "$bento_source" fetch origin main
git -C "$bento_source" worktree add \
"$bento_change" \
-b iac/tide-resource-change \
origin/main
cd "$bento_change"
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"Change only the authority required by the proposed resource:
- Add refresh/read actions to
iac/registrations/policies/tide-shared-services-preview.json. - Add the same reads plus required mutations to
iac/registrations/policies/tide-shared-services-deploy.json. - Use exact resource ARNs. A mutating wildcard boundary must contain the registration ID in its literal prefix or require the exact registration ownership tag.
- Change
iac/registrations/tide-shared-services.tomlonly when its declared deployment, target, ownership, cost, or runtime contract actually changes. A policy expansion does not require a lifecycle transition.
The preview policy must remain read-only. The deploy policy must cover every preview permission. Wildcard actions, unbounded mutation, IAM mutation, access to Bento state, and known delegation actions are refused.
Validate the complete registry and resolved Tide contract:
python3 tools/validate_registered_projects.py
python3 tools/validate_registered_projects.py \
--resolve tide-shared-services
git diff --checkCommit, push, and open a Bento pull request:
git add \
iac/registrations/tide-shared-services.toml \
iac/registrations/policies/tide-shared-services-preview.json \
iac/registrations/policies/tide-shared-services-deploy.json
git commit -m "iac: authorize Tide <resource>"
git push -u origin iac/tide-resource-change
gh pr create \
--repo sharpe-dev/bento \
--base main \
--head iac/tide-resource-change \
--title "iac: authorize Tide <resource>"Omit unchanged paths from git add.
Do not self-approve or merge the Bento pull request. Obtain approval from a human Bento reviewer, wait for required CI, and have the human reviewer merge or queue it. After merge, wait for every triggered registered-IaC authority and GitHub-governance workflow to succeed. Do not proceed until the refreshed Tide contract and target policies have converged.
Implement the Tide resource
Create a separate Tide worktree and branch from current origin/main:
tide_source=/absolute/path/to/an-existing-tide-clone
tide_change=/absolute/path/to/parent/tide-resource-change
git -C "$tide_source" fetch origin main
git -C "$tide_source" worktree add \
"$tide_change" \
-b infra/resource-change \
origin/main
cd "$tide_change"
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"Define the resource in infra/index.ts.
- Keep dependencies pinned in
infra/package.jsonandinfra/package-lock.json. - Use the central default AWS provider; do not create an alternate provider.
- Give resource names a reviewed Tide/registration-specific boundary.
- Set Pulumi
protect: trueand the provider's non-destructive deletion option on durable or stateful resources. - Preserve an existing resource's Pulumi identity. When renaming its logical name or changing its Pulumi type, add an explicit alias and require preview evidence of zero replacement and zero deletion.
- Export stable identifiers that application deployment will consume.
- Never provision secrets as plaintext Pulumi inputs or outputs.
Validate locally:
npm ci --prefix infra
npm run check --prefix infra
npm audit --audit-level=high --prefix infra
actionlint \
.github/workflows/infrastructure-preview.yml \
.github/workflows/infrastructure-deploy.yml
git diff --checkCommit, push, and open the Tide pull request:
git add infra .github/workflows/infrastructure-preview.yml \
.github/workflows/infrastructure-deploy.yml
git commit -m "iac: add <resource>"
git push -u origin infra/resource-change
gh pr create \
--repo sharpe-dev/tide \
--base main \
--head infra/resource-change \
--title "iac: add <resource>"Omit unchanged workflow paths from git add.
Review the registered preview
The infrastructure-preview workflow runs automatically for Tide pull requests
that change infra/.
Require all of the following before merge:
- dependency installation, contract verification, and role assumption succeed;
- the Pulumi operations match the reviewed source;
- creates and updates are expected;
- replacements and deletes are absent unless the change explicitly requires them and has a reviewed migration/recovery procedure;
- no provider deprecation, permission, drift, or policy error is present;
- Tide CI is green.
Do not merge a surprising plan. Fix the Pulumi program or complete a human- approved Bento authority change, then rerun preview.
An authorized Tide maintainer may merge the Tide pull request after its gates pass:
gh pr checks <number> --repo sharpe-dev/tide --watch
gh pr merge <number> --repo sharpe-dev/tide --mergeApprove and verify deployment
A merge to main that changes infra/ starts infrastructure-deploy. It has
two separate approvals:
- An authorized registration owner approves saved-plan generation.
- After the plan artifact is inspected, an authorized registration owner approves application of that exact hashed plan.
Find the run and wait for the saved-plan job:
run_id="$(
gh run list \
--repo sharpe-dev/tide \
--workflow infrastructure-deploy.yml \
--branch main \
--limit 1 \
--json databaseId \
--jq '.[0].databaseId'
)"
gh run view "$run_id" --repo sharpe-dev/tideApprove the registered-iac-tide-shared-services-production environment for
the saved-plan job in GitHub. When that job succeeds, inspect its artifact:
commit_sha="$(
gh run view "$run_id" \
--repo sharpe-dev/tide \
--json headSha \
--jq .headSha
)"
artifact_name="registered-iac-plan-tide-shared-services-${commit_sha}"
evidence_dir="$(mktemp -d /tmp/tide-registered-plan.XXXXXX)"
gh run download "$run_id" \
--repo sharpe-dev/tide \
--name "$artifact_name" \
--dir "$evidence_dir"
jq . "$evidence_dir/manifest.json"
shasum -a 256 "$evidence_dir/pulumi-update.plan"The manifest's changes must match the approved change. Its destructive
array must be empty unless deletion or replacement was explicitly reviewed.
Approve the same protected environment for the apply job only after recording
the exact plan_sha256 and destructive classification in the approval comment.
Wait for completion:
gh run watch "$run_id" --repo sharpe-dev/tide --exit-status
gh run view "$run_id" --repo sharpe-dev/tide --log |
rg "Resources:|unchanged|replication status|error:|warning:"Success requires:
- the saved artifact hash is verified before apply;
- only the saved plan is applied;
- post-apply preview reports no changes;
- the encrypted Pulumi checkpoint reaches recovery replication;
- expected Pulumi outputs identify the deployed resources.
Correct or remove a resource
Use a new Tide pull request. Do not mutate or delete the AWS resource manually.
For stateful resources, define and review data migration, retention, backup, and recovery before approving a replacement or delete. Pulumi protection must be removed only in the same explicitly destructive change.
Keep Bento permissions in place until the resource cleanup has applied and
converged. Contract the Bento policy afterward through another new
origin/main worktree, human-approved Bento pull request, and central authority
convergence.