Skip to content

Runbook: Postmark External Resource Provisioning

Author: Miguel Pinilla Last Verified: 2026-05-05 Environments: all (PostmarkProd serves prod; PostmarkNonProd serves dev and stage)

This runbook is the canonical record for the manual provisioning steps required by Phase 1 of the email-integration project. It covers requirements REQ-EXT-001 through REQ-EXT-005. The operator walks these steps once during Phase B of the project. Subsequent drift-detection is handled automatically by the monthly GitHub Actions workflow added in Phase 1.

This runbook supersedes the parser-gated operator runbook (HUMAN-STEPS.md) used by the prior Phase-0 implementation. The legacy file is deleted as part of the companion Phase 1 infrastructure PR (task T-C6) when that PR merges. There is no infrastructure-side parser gate; sign-off is a human record only (per REQ-OPS-004).

This runbook is addressed to an operator with all three of the following access grants:

  • Postmark Console: owner (or admin) access to the PostmarkProd and PostmarkNonProd accounts.
  • 1Password: access to the Arda-SystemsOAM vault via the desktop application with biometric unlock (DesktopAuth).
  • GitHub: admin access to the Arda-cards/infrastructure repository (required to read and write repository-level Actions secrets).

No AWS access is required for any step in this runbook. The runbook uses three command-line tools: the 1Password CLI (op) for verifying secret references resolve, the GitHub CLI (gh) for the Actions-secret step, and Node.js / npx ts-node if the optional post-provisioning connectivity check (tools/drift-check.ts) is exercised. All three are listed in the pre-flight checklist below.


Before executing any step, confirm the following prerequisites are satisfied:

  • You are logged in to the Postmark Console at https://account.postmarkapp.com with the Arda owner account.
  • You have the 1Password desktop application open and unlocked (biometric unlock).
  • The Arda-SystemsOAM vault is visible in the 1Password sidebar.
  • You have the 1Password CLI (op) installed and signed in (op account list returns the Arda account). Used to verify each op:// reference resolves at the end of the relevant steps.
  • You have gh CLI installed and authenticated: gh auth status returns your GitHub identity with Arda-cards/infrastructure repo scope.
  • You have node and npx available if you intend to run the optional local connectivity check (tools/drift-check.ts) after completing provisioning.

Step 1 — Confirm or Create the PostmarkProd Account (REQ-EXT-001)

Section titled “Step 1 — Confirm or Create the PostmarkProd Account (REQ-EXT-001)”

Goal: a Postmark account named PostmarkProd exists, is active, is on the Platform plan, has 2FA enabled on the owner mailbox, and has a valid account-level API token.

  1. Sign in to the Postmark Console at https://account.postmarkapp.com/login. After login, the Console home (server list) is at https://account.postmarkapp.com/servers; switch accounts from the top-right account selector.
  2. Check whether an account named PostmarkProd already exists.
    • If it exists, click into it and verify:
      • Status is Active.
      • Plan is Platform.
    • If it does not exist, click Create Account, enter name PostmarkProd, and select the Platform plan.

Postmark signup (if starting from scratch): https://account.postmarkapp.com/sign_up. Choose the Platform plan — it is the only plan that supports unlimited servers and domains.

  1. Open the account settings page at https://account.postmarkapp.com/account for the PostmarkProd account.
  2. Confirm that Two-Factor Authentication is enabled. If not, enable it now before proceeding.
  1. Open the account-level API token page at https://account.postmarkapp.com/account/api_tokens for the PostmarkProd account (account-level token, not server-level).
  2. If a token already exists and is captured in 1Password (Step 2 below), skip generation — tokens cannot be retrieved after initial display.
  3. If no token exists (or the existing token has been revoked), click Generate Token. Copy the token immediately — it is shown only once.

Expected state after this step: the PostmarkProd account is active, on the Platform plan, 2FA-enabled, and you hold an account-level API token in your clipboard or a secure note.


Step 2 — Capture the PostmarkProd Token in 1Password (REQ-EXT-002)

Section titled “Step 2 — Capture the PostmarkProd Token in 1Password (REQ-EXT-002)”

Goal: the 1Password item Postmark-Prod in the Arda-SystemsOAM vault contains the PostmarkProd account-level API token in the credential field.

2.1 Locate or create the Postmark-Prod item

Section titled “2.1 Locate or create the Postmark-Prod item”
  1. Open 1Password, navigate to the Arda-SystemsOAM vault.
  2. Search for an item named Postmark-Prod.
    • If the item exists and the credential field contains the current token, confirm the op:// reference resolves (see Step 2.2) and proceed to Step 3.
    • If the item does not exist, create a new Login or API Credential item named exactly Postmark-Prod.

Set the following fields on the Postmark-Prod item:

FieldValueType
credentialThe account-level API token from Step 1.3Concealed (password)
accountOwnerEmailThe email address of the Postmark account ownerText (optional metadata)
planPlatformText (optional metadata)
createdAtCreation date in ISO format (YYYY-MM-DD)Text (optional metadata)

The mandatory field is credential. The optional metadata fields are useful for auditing but are not read by any automated process.

The canonical reference for this item is:

op://Arda-SystemsOAM/Postmark-Prod/credential

You can verify resolution locally:

Terminal window
op read "op://Arda-SystemsOAM/Postmark-Prod/credential"

Expected output: the token value (a 40-character alphanumeric string). If you see an error, re-check vault name, item name, and field name for typos.


Step 3 — Confirm or Create the PostmarkNonProd Account (REQ-EXT-003)

Section titled “Step 3 — Confirm or Create the PostmarkNonProd Account (REQ-EXT-003)”

Goal: a Postmark account named PostmarkNonProd exists, is active, is on the Platform plan, has 2FA enabled, and has a valid account-level API token.

The procedure is identical to Step 1, applied to the PostmarkNonProd account.

  1. Return to the Postmark account list (same URL as Step 1.1).
  2. Check whether an account named PostmarkNonProd exists.
    • If it exists, confirm Status = Active and Plan = Platform.
    • If it does not exist, click Create Account, name it PostmarkNonProd, select Platform plan.

The PostmarkNonProd account serves the dev and stage partitions. It is safe to send test email from this account without production risk. Any sandbox or delivery restriction that Postmark applies by default (e.g., on-trial sending limits) must be resolved before Phase 3 server provisioning.

Same procedure as Step 1.2 for the PostmarkNonProd account.

Same procedure as Step 1.3 for the PostmarkNonProd account. If a token is already captured in Postmark-NonProd (Step 4 below), skip generation.


Step 4 — Capture the PostmarkNonProd Token and Populate All 1Password Items (REQ-EXT-004)

Section titled “Step 4 — Capture the PostmarkNonProd Token and Populate All 1Password Items (REQ-EXT-004)”

Goal: all three 1Password items that Phase 1 declares exist in the Arda-SystemsOAM vault with correct fields.

Same fields as Postmark-Prod (Step 2.2), with the PostmarkNonProd token in the credential field.

Canonical reference:

op://Arda-SystemsOAM/Postmark-NonProd/credential

Verify:

Terminal window
op read "op://Arda-SystemsOAM/Postmark-NonProd/credential"

4.2 Confirm IAC-SCRIPTS Service Account Token

Section titled “4.2 Confirm IAC-SCRIPTS Service Account Token”

This item was created on 2026-04-29 (per Phase 0 record). Confirm it still exists and the credential resolves:

Terminal window
op read "op://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credential"

If the item is missing or the token has expired, generate a new 1Password service account scoped read-only to the Arda-SystemsOAM vault and store it under this item name with the credential field.

Open https://my.1password.com/developer-tools/active/service-accounts to view, create, or rotate service accounts. The IAC-SCRIPTS Service Account Token 1Password item must contain a token issued from this page that has read access to the Arda-SystemsOAM vault.

1Password ItemVaultFieldop:// ReferenceStatus
Postmark-ProdArda-SystemsOAMcredentialop://Arda-SystemsOAM/Postmark-Prod/credentialmust be non-empty
Postmark-NonProdArda-SystemsOAMcredentialop://Arda-SystemsOAM/Postmark-NonProd/credentialmust be non-empty
IAC-SCRIPTS Service Account TokenArda-SystemsOAMcredentialop://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credentialmust be non-empty

The drift-detection workflow asserts every op:// reference declared in platform/one-password.ts resolves to a non-empty value (REQ-CI-002). Phase 1 declares only the three items above; the Free Kanban Tool’s Postmark server token is created by Phase 3 in the separate Arda-CorporateOAM vault per DQ-R1-007 and is not part of Phase 1’s typed surface.

Note — per-partition copies (Phase 4 prerequisite). The Arda-SystemsOAM items above (Postmark-Prod, Postmark-NonProd) are the qualified global-utility items, named with the suffix so Arda-SystemsOAM can hold both Postmark accounts without ambiguity. Following the Arda partition-vault convention, each partition’s Arda-{Env}OAM vault also holds an independently stored copy of its relevant Postmark account token under the service-name-only title Postmark (e.g., op://Arda-ProdOAM/Postmark/credential, op://Arda-DevOAM/Postmark/credential). Those per-partition copies are created as part of Phase 4 partition provisioning. platform/postmark-service.ts will need a partition-aware credential accessor (postmarkCredentialOpReference(partition: PartitionId): string) before Phase 4 deploy tooling can consume them — this is filed as a Phase 4 prerequisite.


Step 5 — Verify OP_SERVICE_ACCOUNT_TOKEN in GitHub Actions (REQ-EXT-005 / REQ-CI-001)

Section titled “Step 5 — Verify OP_SERVICE_ACCOUNT_TOKEN in GitHub Actions (REQ-EXT-005 / REQ-CI-001)”

Goal: the GitHub Actions repository secret OP_SERVICE_ACCOUNT_TOKEN in Arda-cards/infrastructure contains the IAC-SCRIPTS Service Account Token credential so that the drift-detection workflow can authenticate to 1Password in CI.

5.1 Read the service-account token locally

Section titled “5.1 Read the service-account token locally”
Terminal window
OP_SA_TOKEN=$(op read "op://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credential")

5.2 Check whether the secret is already provisioned

Section titled “5.2 Check whether the secret is already provisioned”
Terminal window
gh -R Arda-cards/infrastructure secret list

Look for OP_SERVICE_ACCOUNT_TOKEN in the output. gh secret list does not reveal secret values, only names.

If the secret is absent or stale, provision it using tools/set-gha-repo-secret.sh from the Arda-cards/infrastructure repository:

Terminal window
./tools/set-gha-repo-secret.sh \
--repo Arda-cards/infrastructure \
--secret-name OP_SERVICE_ACCOUNT_TOKEN \
--from-1password "op://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credential"

The script does the op read | gh secret set translation for you, plus preflight checks that op and gh are installed and authenticated. To verify everything resolves before writing to GitHub, append --dry-run:

Terminal window
./tools/set-gha-repo-secret.sh \
--repo Arda-cards/infrastructure \
--secret-name OP_SERVICE_ACCOUNT_TOKEN \
--from-1password "op://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credential" \
--dry-run

Alternatively, provision directly via the GitHub CLI without the helper script:

Terminal window
op read "op://Arda-SystemsOAM/IAC-SCRIPTS Service Account Token/credential" | \
gh -R Arda-cards/infrastructure secret set OP_SERVICE_ACCOUNT_TOKEN

After provisioning, re-run gh secret list and confirm OP_SERVICE_ACCOUNT_TOKEN appears. You can also trigger a workflow_dispatch run of the drift-detection workflow after Phase 1 Task 4 has landed to confirm end-to-end resolution.

Terminal window
gh -R Arda-cards/infrastructure secret list

Expected output: a table row containing OP_SERVICE_ACCOUNT_TOKEN with a recent update timestamp.


Post-Provisioning Connectivity Check (Optional but Recommended)

Section titled “Post-Provisioning Connectivity Check (Optional but Recommended)”

After completing Steps 1—5, run the local connectivity check to confirm all external resources are reachable with the configured credentials:

Terminal window
cd <path-to-infrastructure-worktree>
OP_ACCOUNT_NAME=Arda npx ts-node tools/drift-check.ts

Expected output: a JSON report with all items showing status: "ok" and Postmark API calls returning 2xx. This is the same check the monthly drift workflow runs in CI.


SymptomLikely CauseResolution
op read returns [ERROR] Item "Postmark-Prod" not foundItem name typo or wrong vaultVerify exact item name (case-sensitive) and vault name in 1Password sidebar
op read returns [ERROR] You are not signed in1Password desktop app lockedUnlock the desktop app via biometrics, then retry
op read returns [ERROR] Vault "Arda-SystemsOAM" not foundService account lacks vault accessRe-check service account scoping in the 1Password developer portal; vault must be listed under read-only access
Postmark API returns 401 Unauthorized on connectivity checkStale or revoked token in credential fieldGenerate a new account-level token in the Postmark Console and update the 1Password item; the old token cannot be recovered
Postmark API returns 422 or plan-related errorAccount is not on Platform planUpgrade the account in the Postmark Console: review billing at https://account.postmarkapp.com/account/billing_settings and change the plan at https://account.postmarkapp.com/account/subscription
gh secret list does not show OP_SERVICE_ACCOUNT_TOKENSecret was never provisioned, or was deletedRun Step 5.3 to provision it
gh secret set fails with HTTP 404gh is not authenticated with sufficient scopeRe-authenticate: gh auth login and confirm repo scope; check your GitHub account has admin access to Arda-cards/infrastructure
tools/drift-check.ts exits non-zero in CI but passes locallyOP_SERVICE_ACCOUNT_TOKEN value in GHA does not match current 1Password itemRotate the GHA secret: read the current value from 1Password and re-run Step 5.3
1Password service account token returns forbidden on item readService account scope excludes Arda-SystemsOAMIn the 1Password developer portal, add read-only access for Arda-SystemsOAM to the service account, then rotate the token and re-provision the GHA secret
Step 1.2 / 3.2 — 2FA toggle not visible at https://account.postmarkapp.com/accountPostmark’s UI exposes 2FA on the user-profile page, not the account-settings page. The status is observable on the account’s user list (e.g., https://account.postmarkapp.com/account/users), but the enable action lives on the user’s own profile, reached via the top-right user-avatar menu in the Console.Locate the user profile via the avatar menu and follow Postmark’s 2FA setup flow. Status returns to “On” on the user list once enabled. The exact URL is per-account and per-user; record it in this row’s resolution column when found, and update the runbook on the next walkthrough.

Post-Merge: First Drift-Workflow Run (T-C5) and GHA Secret Audit (T-C7)

Section titled “Post-Merge: First Drift-Workflow Run (T-C5) and GHA Secret Audit (T-C7)”

Two operator actions complete Phase 1 once Arda-cards/infrastructure PR #446 merges to main:

Before merge, GitHub Actions requires the workflow file to land on the default branch before workflow_dispatch will accept it; pre-merge dispatches return HTTP 404 -- workflow not found on the default branch. Run this once immediately after PR #446 merges:

Terminal window
gh workflow run external-resources-drift.yml -R Arda-cards/infrastructure
gh -R Arda-cards/infrastructure run list --workflow external-resources-drift.yml --limit 1
gh -R Arda-cards/infrastructure run watch <run-id>

Expected: workflow completes successfully. The drift-check step prints a JSON report with passed: 5, failed: 0 and the workflow exits 0. Verifies V-CI-102 (workflow first run completes successfully).

On failure: read the failed step’s logs (gh -R Arda-cards/infrastructure run view <run-id> --log-failed). The most likely first-run failure is the GHA secret being out of sync with the rotated 1Password value — re-run Step 5.3 above to refresh, then re-trigger the workflow.

T-C7 — Confirm no Postmark-token GHA secrets

Section titled “T-C7 — Confirm no Postmark-token GHA secrets”
Terminal window
gh -R Arda-cards/infrastructure secret list | grep -i postmark

Expected: zero matches. The rev1 design resolves Postmark account tokens at runtime via the 1Password SDK using OP_SERVICE_ACCOUNT_TOKEN; no Postmark token is ever stored in GitHub Actions secrets (REQ-CI-003, V-CI-103). If a Postmark-named secret appears, it is leftover from a prior provisioning approach and should be deleted with gh -R Arda-cards/infrastructure secret delete <NAME>.

The 2026-05-05 walkthrough surfaced one such leftover (POSTMARK_NONPROD_ACCOUNT_TOKEN, set 2026-04-30 from the prior Phase-0 era) and deleted it. Future operators should treat any new appearance as a regression to investigate.


Looking Ahead: Domain Verification (Phase 3 / Phase 4)

Section titled “Looking Ahead: Domain Verification (Phase 3 / Phase 4)”

The Postmark accounts you have just provisioned (PostmarkProd, PostmarkNonProd) are usable for API operations today — token resolution, server creation, sender-signature listing, and the drift-detection probe. They are not yet usable for live mail delivery to arbitrary recipients. Postmark restricts outbound delivery on a new account until at least one sending domain is verified under that account; a fresh account is effectively a sandbox.

Domain verification is operator-driven and happens in subsequent phases:

  • Phase 3 (Corporate Updates): verify arda.ardamails.com (the Corporate zone parent) under the PostmarkProd account. The Phase 3 Corporate stack emits the required DKIM TXT and Return-Path CNAME records into the arda.ardamails.com zone (via the dns-email-records.ts construct); the operator clicks “Verify” in the Postmark Console and, if needed, submits an account approval request to take PostmarkProd out of sandbox.
  • Phase 4 (Runtime Platform Updates): per partition, verify {partition}.ardamails.com (e.g., prod.ardamails.com, dev.ardamails.com) under the appropriate Postmark account. Same shape: CDK emits records, operator clicks “Verify”.

The detailed checklist is being authored at roadmap/in-progress/email-integration/3-corporate-updates/operator-domain-verification-checklist.md (Phase 3 planning area). It will be fleshed out during Phase 3 planning; Phase 1 surfaces it now so operators know about the upcoming work, the DNS access required, and the Postmark Console clicks to come.

References:


Arda’s mail traffic is transactional only. Every Postmark server configured for Arda — the Free Kanban Tool server today, future per-partition operations servers, future per-tenant servers (Phase 5b) — uses the default Transactional Message Stream. Do not repurpose any of these servers (or their default streams) for marketing, newsletter, or bulk/broadcast sends.

If a future use case requires bulk send (tenant announcements, product updates, periodic digests), provision a separate Broadcast Message Stream on the relevant server per Postmark’s policy (Best Practices for Bulk/Broadcast Sending; How to create and send through message streams). The Broadcast stream is operationally distinct from the Transactional stream — its own throttle, its own suppression list, its own reputation. Co-mingling the two on a single stream both violates Postmark’s policy and risks degrading transactional deliverability for the entire account.

Operator implications:

  • When approving a new send-path proposal, confirm the traffic is genuinely transactional (1-to-1, triggered by user action, non-promotional). If it is bulk, the design must specify a dedicated Broadcast stream and a separate operational surface; do not approve “ship it through the existing transactional stream” as a shortcut.
  • When investigating a deliverability incident, check the message-stream classification of the offending sends. Broadcast traffic landing on the transactional stream is one of the first hypotheses to rule out.
  • Postmark’s account-approval correspondence (e.g., the May 2026 arda-prod approval reply) explicitly lists Broadcast Stream discipline as a deliverability prerequisite — treat it as a standing operational invariant, not a one-time recommendation.

This invariant is also recorded in the project’s cross-cutting design § 7a and is a constraint inherited by Phase 5b’s EmailSender API.


Record sign-off once all steps are complete and the connectivity check is green. There is no automated gate — this table is the auditable human record. Partial completions are recorded with the partial state in the Deviations column and a forward-pointer in Notes.

StepRequirementOperatorDate (YYYY-MM-DD)DeviationsNotes
1 — PostmarkProd accountREQ-EXT-001Miguel Pinilla2026-05-05None2FA enabled per Step 1.2; Account-level token generated.
2 — PostmarkProd token in 1PasswordREQ-EXT-002Miguel Pinilla2026-05-05Noneop://Arda-SystemsOAM/Postmark-Prod/credential resolves; drift-check probe HTTP 200.
3 — PostmarkNonProd accountREQ-EXT-003Miguel Pinilla2026-05-05Partial — 2FA toggle not located in the Postmark Console UI for this account on the date of walkthrough; account otherwise on Platform plan with valid account-level token.2FA enable URL TBD; deferred. The Postmark-NonProd token resolves and the drift-check probe returns HTTP 200, so Phase 1’s downstream consumers (drift workflow, Phase 4 deploy) are not blocked. Re-run this row when the 2FA toggle is located.
4 — All 1Password items populatedREQ-EXT-004Miguel Pinilla2026-05-05NoneAll three Phase-1 items (Postmark-Prod, Postmark-NonProd, IAC-SCRIPTS Service Account Token) populated in Arda-SystemsOAM; Free-Kanban-Generator-Postmark-Server is a Phase 3 deliverable in Arda-CorporateOAM per DQ-R1-007.
5 — GHA secret provisionedREQ-EXT-005 / REQ-CI-001Miguel Pinilla2026-05-05NoneOP_SERVICE_ACCOUNT_TOKEN set on Arda-cards/infrastructure via tools/set-gha-repo-secret.sh.
Post-provisioning connectivity checkAll aboveMiguel Pinilla2026-05-05Nonenpx ts-node tools/drift-check.ts returned 5 passed / 0 failed.
T-C5 — First workflow_dispatch run (post-merge)REQ-CI-002 / V-CI-102Deferred — requires PR #446 to merge to main first; GitHub Actions returns HTTP 404 for workflow_dispatch against a workflow file not on the default branch.Run once immediately after merge; capture run-id and link in Notes.
T-C7 — gh secret list audit (Postmark name match)REQ-CI-003 / V-CI-103Miguel Pinilla2026-05-05NoneAudit returned 1 leftover (POSTMARK_NONPROD_ACCOUNT_TOKEN from the prior Phase-0 era); deleted with gh secret delete. Re-audit returned zero matches.