Getting started
Quickstart
Everything runs on your machine against synthetic fixtures. No account, no sales call, no signed terms.
#Prerequisites
Docker Desktop, or any Docker Engine with Compose v2. Java and Node are not required to run the container stack — only to run the verification suites on the host.
127.0.0.1:8081 — loopback only#Start the sandbox
From the repository root:
# Windows (PowerShell)
.\scripts\dev-up.ps1
# macOS / Linux
./scripts/dev-up.sh
Each script creates .env from the synthetic .env.example when it is missing, brings the stack up with --wait, and verifies both health probes return 200:
docker compose --env-file .env up --build --detach --wait
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/health/live
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/health/ready
The checked .env.example values are intentionally public local fixtures and are unsuitable for any shared environment. The documented local bearer value is local-sandbox-token. The backend has no published host port; the Nginx edge on 127.0.0.1:8081 is the only HTTP application origin.
#Create a synthetic draft
Every draft creation requires a bearer credential and an Idempotency-Key. The only medication fixture that resolves successfully is NDC 00000000000.
curl -X POST http://127.0.0.1:8081/api/v1/prescription-drafts \
-H "Authorization: Bearer local-sandbox-token" \
-H "Idempotency-Key: 8f2b1c44-0d3e-4a6f-9b21-77c5e0a1d934" \
-H "Content-Type: application/json" \
-d '{
"practiceId": "prc_demo",
"patientId": "pat_demo",
"prescriberId": "pre_demo",
"medication": { "ndc": "00000000000" },
"quantity": { "value": 30, "unitCode": "TABLET" },
"directions": { "text": "Take one tablet by mouth once daily" },
"refills": 1,
"substitutionAllowed": true,
"pharmacyId": "phm_demo",
"diagnoses": [
{ "codeSystem": "ICD10CM", "code": "Z00.00", "display": "General adult medical examination" }
]
}'
A 201 response returns the created draft plus a short-lived review URL. The review secret lives in the URI fragment, so it is never transmitted to the server:
/prescribe/review/{publicGrantId}#secret={opaqueSecret}
#Open the review
Paste that URL into a browser. The client strips the fragment before React mounts, exchanges the secret exactly once for a device-bound session cookie, and renders the immutable review projection.
The grant is valid for five minutes and is single-use. A second exchange attempt fails closed with a coarse 410, as does an expired, unknown, or tampered grant — the responses are deliberately indistinguishable.
#Reset and stop
./scripts/dev-down.sh # stop, keep the local database volume
./scripts/dev-reset.sh # stop AND delete the synthetic database volume
./scripts/smoke-test.sh # probe liveness and readiness
dev-reset destroys the local synthetic database and asks for confirmation first; pass --force (or -Force on PowerShell) in automation. It never touches anything outside this Compose project.
#Run the verification suites
| Suite | Command | Docker |
|---|---|---|
| Java unit, architecture, contract, integration | ./gradlew checkAll --no-daemon | Required |
| Java Docker-free subset | ./gradlew test --no-daemon | Not required |
| Web types, unit, build | npm run typecheck && npm test && npm run build | Not required |
| Browser end-to-end | npm run test:e2e | Chromium via Playwright |
| Contracts | npm run verify | Not required |
| Documentation links | node scripts/check-doc-links.mjs . | Not required |
As of the most recent clean-cache audit run, the Java suites pass 102 unit, architecture, and contract tests plus 16 PostgreSQL integration tests with zero failures, errors, or skips; the web suite passes 30 of 30; and dependency audits report zero high-severity vulnerabilities.
#Troubleshooting
| Symptom | Likely cause |
|---|---|
| Startup fails immediately | Non-synthetic data detected, or an environment other than SANDBOX. Startup validation refuses to boot rather than run in an unsafe configuration. |
Readiness never returns 200 | PostgreSQL has not finished migrating. Flyway runs on a separate datasource before the application accepts traffic. |
Review link returns 410 | The grant expired (five minutes), was already consumed, or the draft expired (24 hours). All failure modes return the same coarse response by design. |
Draft creation returns 409 | The idempotency key was reused with a materially different request body. |
| Volume incompatibility on first start | A database volume created before the namespace cutover cannot be attached. Use a fresh synthetic volume with COMPOSE_PROJECT_NAME=fast-scripts-sandbox. |