Skip to content

Phase 1 -- External Resources Provisioning -- Specification

Implementation specification for the work that brings the external resources, repository declarations, CI surface, and operator-facing documentation into the state required by requirements.md. The cross-phase contracts produced are catalogued in exports.md; the verification strategy and tests are in verification.md. Current-state context is in analysis.md.

This specification is target-state only. Where the analysis identifies partial existing artifacts, the specification states the destination; the implementor may reuse, port, or rebuild as appropriate.

  • Project type: Modification (per analysis.md).
  • Repositories touched: /infrastructure (configuration files, drift workflow), /documentation (operator-facing pages). No work in /operations or /common-module.
  • Worktree strategy: Single worktree per repository; no multi-agent parallelism for Phase 1. (The phase is operator-driven and the code surface is small.)
  • Effort shape: ~5 tasks across two repositories, primarily declarative additions to existing files plus three new documentation pages and one new GitHub Actions workflow.

The implementor follows the conventions established in:

  • The instructions/repositories.md file in the workspace repo (Arda-cards/workspace) — repo overview.
  • The instructions/dev-workflows.md file in the workspace repo — build, test, deploy commands.
  • typescript-coding skill — TypeScript style for the platform/ files.
  • unit-tests-infra skill — conventions for unit tests added under infrastructure.
  • document-writing + path-conventions skills — conventions for the documentation pages.

CHANGELOG entries are required in every repository whose code changes; see the per-task acceptance criteria.

The tasks below are sequenced. Each lists scope, source references, acceptance criteria, and STOP points where the operator must explicitly confirm before the next task begins. Per-task verification appears in verification.md; the test identifiers (V-XXX-NNN) are forward references.

Task 1: Confirm and complete external-resource provisioning (operator)

Section titled “Task 1: Confirm and complete external-resource provisioning (operator)”

Requirements covered: REQ-EXT-001REQ-EXT-005; REQ-CI-001.

Scope: the operator confirms or completes the manual external-resource provisioning, recorded in the canonical runbook at /documentation/src/content/docs/current-system/oam/postmark-service/operator-runbook.md (delivered by Task 5 below). For Phase 1’s first execution the runbook is followed in draft form during Task 5’s authoring.

  1. PostmarkProd: confirm the account exists, is on the Platform plan, has 2FA enabled on the owner mailbox, and that the account-level API token captured in 1Password (Postmark-Prod item) authenticates against the Postmark Account API.
  2. PostmarkNonProd: same as above for the NonProd account. Create the account if missing.
  3. 1Password items: confirm Postmark-Prod, Postmark-NonProd, IAC-SCRIPTS Service Account Token, and the Free Kanban Generator Postmark placeholder item exist with the required field shapes.
  4. Service-account access: confirm the 1Password service-account token has read-only access to the Arda-SystemsOAM vault and resolves at least one item via the SDK.
  5. GitHub Actions secret: confirm OP_SERVICE_ACCOUNT_TOKEN is provisioned in the Arda-cards/infrastructure repository and its value matches the IAC-SCRIPTS Service Account Token 1Password item. If missing or stale, provision it using the existing tools/gha-secret.ts utility (the utility itself is assumed in place; its retirement and any path migration is out of scope of this project).
  • All REQ-EXT-NNN items reach the “ok” status in analysis.md § Summary Table.
  • The operator records sign-off in the canonical runbook (delivered by Task 5).

After Task 1, the operator confirms with the rest of the team that the external resources are ready. No subsequent task should run before this confirmation.

Task 2: Enrich platform/postmark-service.ts

Section titled “Task 2: Enrich platform/postmark-service.ts”

Requirements covered: REQ-PLAT-001.

Scope: extend the existing infrastructure/src/main/cdk/platform/postmark-service.ts file with the API surface metadata.

The existing file declares PostmarkAccount plus the two account constants. Add:

  • POSTMARK_ACCOUNT_API_BASE_URL: string — value "https://api.postmarkapp.com".
  • POSTMARK_PLAN: string — value "Platform".
  • A POSTMARK_API_SURFACE constant of a typed shape with at least:
    • freshnessDate: string (ISO date) — the date as of which the API surface assumptions were last verified against Postmark’s published documentation.
    • observationsNotePath: string — the path to current-system/oam/postmark-service/postmark-api-observations.md (relative to the documentation root) so the constant points readers to the design-intent note.

The exact TypeScript shape (interface vs type vs as const literal) is left to the implementor’s judgement; the values must be exported for downstream consumption.

  • Existing file: /infrastructure/src/main/cdk/platform/postmark-service.ts
  • Sibling pattern: /infrastructure/src/main/cdk/platform/aws-configuration.ts, /infrastructure/src/main/cdk/platform/web-configuration.ts
  • All exports above present and typed.
  • tsc --noEmit passes in /infrastructure.
  • A unit test (V-PLAT-001, see verification.md) verifies the shape and content of the new exports.
  • CHANGELOG entry in /infrastructure under “Added”.

Requirements covered: REQ-PLAT-002, REQ-PLAT-003.

Scope: extend the existing infrastructure/src/main/cdk/platform/one-password.ts with typed constants for every 1Password item the project will reference.

The existing file exports only OAM_VAULT. Add typed constants for the four items:

  • POSTMARK_PROD_ITEM — title "Postmark-Prod", vault OAM_VAULT, primary field "credential", full reference "op://Arda-SystemsOAM/Postmark-Prod/credential".
  • POSTMARK_NONPROD_ITEM — analogous for Postmark-NonProd.
  • IAC_SCRIPTS_SERVICE_ACCOUNT_ITEM — analogous for IAC-SCRIPTS Service Account Token.
  • FREE_KANBAN_POSTMARK_ITEM — analogous for Free Kanban Generator Postmark.

A typed shape (OnePasswordItem interface) is recommended:

export interface OnePasswordItem {
readonly vault: string;
readonly title: string;
readonly primaryField: string;
readonly reference: string; // op:// URI for the primary field
}

The reference field is a derived value; it must be consistent with vault, title, and primaryField.

  • Existing file: /infrastructure/src/main/cdk/platform/one-password.ts
  • All four items declared as typed OnePasswordItem constants plus the existing OAM_VAULT.
  • tsc --noEmit passes.
  • A unit test (V-PLAT-002) verifies the shape, vault, and reference consistency of each constant.
  • CHANGELOG entry in /infrastructure under “Added”.

Task 4: Drift-check module and drift-detection workflow (dual-purpose)

Section titled “Task 4: Drift-check module and drift-detection workflow (dual-purpose)”

Requirements covered: REQ-CI-002, REQ-CI-003; also delivers the connectivity-test runner consumed by V-EXT-001V-EXT-005 and V-CI-101.

Scope: a single TypeScript module that (i) operators can run locally to verify external-resource state, and (ii) a GitHub Actions workflow invokes on a monthly schedule. The same code in two invocation contexts. Plus the workflow file itself.

A single TS module with one public entry point. Its body:

  • Imports the constants from platform/postmark-service.ts and platform/one-password.ts.
  • Resolves each declared op:// reference using the 1Password SDK. The SDK’s auth source is auto-detected:
    • Local-dev: DesktopAuth (biometric unlock against the operator’s 1Password app).
    • CI: OP_SERVICE_ACCOUNT_TOKEN env var (the established convention from the prior implementation’s infrastructure/scripts/lib/one-password.ts).
  • Asserts each reference returns a non-empty value.
  • For each Postmark account whose credential resolves, issues a benign Postmark Account API call (GET /servers?count=1) and asserts a 2xx response.
  • Exits zero on full success; exits non-zero with a structured error report (JSON to stdout) on any failure.

The module is testable with mocked 1Password SDK and mocked HTTP client (see V-CI-001V-CI-003). Operator invocation: npx ts-node tools/drift-check.ts. CI invocation: a single npx ts-node step in the workflow.

This is the dual-purpose point: V-EXT-001 — V-EXT-005 (operator-runnable connectivity tests) and the drift workflow’s body are the same code. Operators and CI read identical structured output.

Drift-detection workflow (.github/workflows/external-resources-drift.yml, filename per OQ-1)

Section titled “Drift-detection workflow (.github/workflows/external-resources-drift.yml, filename per OQ-1)”
  • Triggers: schedule (cron: '0 9 1 * *' — 09:00 UTC on the 1st of each month, matching the Postmark-foundations integration cadence) and workflow_dispatch (operator-triggered ad hoc).
  • Permissions: contents: read, issues: write (for the failure-issue step).
  • Steps:
    1. Checkout the repository.
    2. Install infrastructure dependencies (npm ci).
    3. Invoke npx ts-node tools/drift-check.ts with OP_SERVICE_ACCOUNT_TOKEN in env.
    4. On failure: a follow-up step opens a GitHub issue using gh issue create, with title Drift detected in external resources (run YYYY-MM-DD), body containing the run URL and the failure report, and labels drift,phase-1,external-resources.
  • Postmark-foundations workflow as a template: /infrastructure/.github/workflows/postmark-foundations.yml (from the prior Phase-0 implementation; uses the gh issue create pattern on schedule failures).
  • 1Password SDK usage: /infrastructure/scripts/lib/one-password.ts (from the prior Phase-0 implementation; reusable wrapper — the dual-auth discovery logic informs the new module).
  • The drift-check module exists at tools/drift-check.ts (filename per OQ-2) with a documented public entry point.
  • The workflow file exists, validates against the GitHub Actions schema (no actionlint errors if available).
  • Unit tests against the drift-check module pass: V-CI-001, V-CI-002, V-CI-003.
  • A first manual run (workflow_dispatch) completes successfully against the live external resources after Task 1’s sign-off (V-CI-101, V-CI-102).
  • An operator can run npx ts-node tools/drift-check.ts locally with DesktopAuth resolution and the script reports green against the live external resources (operator validation of V-EXT-001V-EXT-005).
  • CHANGELOG entry in /infrastructure under “Added”.

Requirements covered: REQ-OPS-001REQ-OPS-004, REQ-DOC-001REQ-DOC-004.

Scope: three new documentation pages in /documentation/src/content/docs/current-system/oam/postmark-service/. Plus one update to current-system/oam/index.md to discover the new section.

  1. index.md — Postmark service overview. Covers account topology, credential storage, OAM model (Postmark Console as primary surface), drift cadence. Sized ~2 pages.
  2. postmark-api-observations.md — Postmark API observations note. Covers authentication models for API and Webhooks, error model, idempotency / retry conventions, webhook payload shapes, version-pin assumptions. Cross-links to Postmark official docs for the surface itself. Sized ~3 pages (per REQ-DOC-003).
  3. operator-runbook.md — canonical operator runbook for the manual external-resource provisioning steps. Covers all manual steps required by REQ-EXT-001-REQ-EXT-005, in operator-execution order. Includes a per-step troubleshooting table and a sign-off section. Sized ~3-4 pages.

The runbook supersedes infrastructure/scripts/postmark-foundations/HUMAN-STEPS.md. The infrastructure-side file is deleted as part of this task (it was a parser-gated artefact whose gate is retired); a redirect from its former location is not required because no external link refers to it.

If current-system/oam/index.md exists and indexes its sub-sections, add an entry for postmark-service/. If it does not exist or does not index sub-sections, no action; Starlight auto-discovers child pages.

  • All three pages exist, well-formed, with the required content per requirements.md.
  • make pr-checks passes in /documentation (lint, link check, smoke tests).
  • infrastructure/scripts/postmark-foundations/HUMAN-STEPS.md is removed; no remaining code in /infrastructure parses a HUMAN-STEPS file as a runtime gate (verified by grep).
  • CHANGELOG entries: one in /documentation under “Added”; one in /infrastructure under “Removed” (for the deleted HUMAN-STEPS file and any associated parser code).

After Task 5 lands in /documentation, the operator runbook is reviewable as a coherent end-to-end document. Confirm with reviewers before declaring Phase 1 complete.

Restated for clarity; identical to requirements.md § Out of scope of Phase 1.

  • Postmark server creation (Phase 3 / Phase 4).
  • Per-partition Secrets Manager / encryption keys (Phase 4).
  • Decisions about retiring tools/gha-secret.ts (a future project).
  • Helm chart additions (Phase 5b).
  • Migration of any existing infrastructure/scripts/gha-secrets/ implementation to infrastructure/tools/gha-secret.ts (a side concern; the spec does not depend on that migration happening during Phase 1).
#QuestionOptionsRecommendationDecision
OQ-1Final filename of the drift workflowexternal-resources-drift.yml; phase-1-drift.yml; op-drift.ymlexternal-resources-drift.yml — describes the asserted invariant, not the phaseDecided — see DQ-R1-001
OQ-2Final location of the drift-check TypeScriptinfrastructure/scripts/drift-check.ts; infrastructure/tools/drift-check.tstools/drift-check.ts — it is operator-runnable in addition to CI-runnable; consistent with the tools/ conventionDecided — see DQ-R1-002
OQ-3Where to surface the runbook’s sign-off mechanismA code block; a YAML frontmatter field; a designated tableA designated Markdown section (“Operator Sign-off”) with a small table for name / date / deviationsDecided — see DQ-R1-003
OQ-4Whether to delete the existing infrastructure/scripts/postmark-foundations/HUMAN-STEPS.md and its parser in this phase, or to defer the parser deletionDelete in Task 5; defer parser deletion to a follow-upDelete in Task 5 — per REQ-OPS-004, no parser gate remainsDecided — see DQ-R1-004
OQ-5API-surface freshness cadenceAnnually; at each Postmark major-update post; on first drift-test failure attributable to surface driftAt first drift-test failure attributable to surface drift, augmented by an annual reviewDecided — see DQ-R1-005

The Open Questions table records decisions as they are resolved during implementation. Each decision is recorded in the project-level decision log using the DQ-R1-NNN numbering scheme (per ../architecture-overview.md § 10).