GPLink Phase 1 · SOW-B
The server architecture behind the integration layer between the clinical side, the commerce side and the payer — drawn as trust boundaries rather than as a vendor's product catalogue.
The architecture as it is specified and costed — capability by capability, with no provider named. This is the version the quotation is written against.
The same architecture as it would be built on AWS in the Malaysia region. Nothing in the design changes; only the names of the managed services behind each box.
Three vendor systems, one platform, four stores. The heavy line around the middle is a trust boundary, not a network diagram convenience — what crosses it is enumerated, and what does not cross it is the point.
← Click Interactive to walk the diagram: five guided views, presenter mode, PNG and SVG export.
ap-southeast-5 — ASIA PACIFIC (MALAYSIA)
An in-country region with three Availability Zones, generally available since August 2024. Around 150 AWS services run in-region, including every managed service this architecture depends on.
What the topology does, rather than what it contains. One money-bearing call — dispense.partial — from the vendor's network to a durable row, then the asynchronous half that cannot change the answer the vendor already has.
← Click Interactive to walk the diagram: five guided views, presenter mode, PNG and SVG export.
202 already returned. The five chips inside the gateway are the guards, each with the status code it answers with.Prime Commerce presents OAuth2 client credentials; FEV3R presents mTLS; the payment gateway an API key + HMAC. No shared secret spans two vendors.
Managed rule sets and a rate rule per partner, then TLS termination and a healthy target. Idle timeout is set above the slowest vendor call, so a slow partner never reads as a 504.
Each returns its own status code, shown on the chips. The 409 is the one that matters: same idempotency key, different body — nothing is processed, because handling that politely is how a pharmacy gets paid twice.
This is the decision the design rests on. It is why replay comes from PostgreSQL rather than a queue with a retention window, and why a partner's retry storm is cheap instead of dangerous.
Accepted means stored, not processed. Nothing downstream can make this response wrong — which is the whole reason the write comes first.
MessageGroupId = episode_id: strict order inside one episode, parallel across episodes. The guarantee is a queue attribute, not a convention a developer has to remember.
Step committed, compensation armed, split rules applied. This is the only service allowed to write money.
Double entry, both sides, one episode id. If the ledger and the rule engine disagree, settlement halts — it does not warn and continue.
Outbound calls to FEV3R, commerce or the courier carry their own idempotency key and their own retry budget.
On exhausted retries: full envelope, attempt history, and a replay a named operator presses. The replay is itself an audited action with its own event.
Sizing comes from the quotation's infrastructure sheet, where every line carries a unit price and a quantity. Change the quantity, not the total.
Sizing comes from the quotation's infrastructure sheet, where every line carries a unit price and a quantity. The right-hand column is the AWS service each line resolves to and the configuration it runs at — the sizing is unchanged by it.
| Component | One unit is | MVP | Sizing basis | On AWS · configuration |
|---|---|---|---|---|
| Compute | App instance, 2 vCPU / 4 GB, container runtime | 4 | Gateway, orchestrator, identity, master data | 4 tasks · rolling deployment with circuit-breaker rollback · images immutable, scanned on push · no host to patch |
| Managed PostgreSQL | 2 vCPU / 8 GB + 200 GB storage + backup | 1 | One primary with daily backup; a read replica at FULL | Multi-AZ · 200 GB gp3 · point-in-time recovery · no UPDATE grant on the ledger table |
| Redis | Cache node, 2 GB | 1 | Idempotency keys, sessions, hot inventory cache | One node, shared across tasks so a deploy never loses a key · keys expire on a fixed window, longer than any partner retry cycle |
| Message broker allowance | Managed queue or stream, ~10M messages a month. Held as an allowance, not a metered pass-through — it covers the upgrade to a streaming cluster should volume require it. | 1 | One message per handoff event |
|
| Object storage | 500 GB stored plus egress | 1 | Audit archive, payout files, onboarding documents | Versioning on · Object Lock on the audit prefix · lifecycle to Glacier |
| Observability | 25 GB ingested a month, 30-day retention | 2 | 50 GB at 30 days; 125 GB at 90 days for traces at FULL | OpenTelemetry writes here, so the backend can change without touching application code |
| Load balancer, CDN, WAF | LB + 200 GB CDN + WAF rule set | 1 | Sized on page and API traffic | HTTPS only · idle timeout above the slowest vendor call · managed rules plus per-partner rate rules |
| Secrets manager & KMS | 50 secrets + 5 encryption keys | 1 | One secret per integration, per environment | Injected at task start, never in an image or repository · key policy separate from the application role · expiry alerts before it expires |
| Backup, snapshots, DR | 500 GB snapshot storage | 1 | A warm standby in a second zone at FULL | Cross-AZ snapshots |
Production is not a special snowflake built by hand. It is the same infrastructure template with different inputs — which is the only reason a cutover rehearsal means anything, and why the build-phase figure is a fraction of the run rate.
100%
Live traffic, switched on at cutover. Its own account, its own VPC, Multi-AZ database. No credential is shared with a lower environment.
40%
The shared three-vendor sandbox. Same services, smaller instance classes, single-AZ database.
20%
Ephemeral. Torn down and rebuilt from the same template. The FEV3R mock and contract tests live here, so one team never waits on another.
Three lifecycles in one picture: a secret reaching a running process, a partner credential being enforced on every call, and what actually happens in the hour a credential leaks.
← Click Interactive to walk the diagram: five guided views, presenter mode, PNG and SVG export.
Each vendor's own credential type. No shared secret spans two vendors, so a compromise at one company cannot be replayed at another.
Managed rule sets plus a rate rule per partner — a coarse net before the gateway's precise one.
Its name, scope and key policy are reviewed like code. The value never enters the repository, the image or a CI variable.
Encrypted under a customer-managed key whose policy is separate from the application role. The task definition carries the ARN; only the running task sees the value, and the logger redacts it.
The same write as step 4 of the request path — here it is what makes a replay after containment possible weeks later.
Allowed or refused, with the correlation id. Secrets are redacted by the logger, not by developers remembering to.
Allow-list miss, rate anomaly, or a credential used from two regions. The gateway refuses that credential on the next request — no deploy, no restart, no code change.
Everything the suspended partner sends is dead-lettered with its full envelope. Containment costs availability, never data.
A new secret version, the task restarted, the old version disabled — then the held events replay. One secret per integration keeps the blast radius one partner wide.
Suspension, rotation and each replay are audit lines of their own, archived under Object Lock. Dead-letter depth is itself an alarm.
Third parties are the largest attack surface in a three-company platform, because each one is a door somebody else holds the key to. A credential's rights come from its stage, not from its validity.
| Stage | May do | Exit condition |
|---|---|---|
| Sandbox | Sandbox endpoints and scopes only. No route to production traffic. | Conformance suite runnable end to end |
| Certification | Real contracts, sandbox traffic. The key expires on a date whether or not certification finishes. | All five conformance checks pass. Not four. |
| Live | Production credential and scopes, with IP allow-list and per-endpoint rate limits enforced on every call. | Breach, expiry or offboarding |
| Suspended | Nothing. Inbound events are dead-lettered, not discarded — a suspension must never lose a partner's data. | Reinstatement or offboarding |
| Offboarded | Nothing. Records retained per policy; credentials revoked and keys destroyed. | Terminal |