Date: 2026-03-19 Status: Approved
Run a single orcha instance on Tolaria (Raspberry Pi 5, 8GB RAM, Debian Trixie aarch64) accessible at *.demo.getorcha.com, with PostgreSQL and LocalStack in Docker and the orcha JVM running directly on the host. PDFs served via NPM's sub_filter rewriting + local proxy to LocalStack — no S3 endpoint exposed to the internet. NPM proxy hosts created automatically via API.
app.demo.getorcha.comBrowser
│ HTTPS
▼
DNS (Route53)
*.demo.getorcha.com → home IP
*.tdev.getorcha.com → home IP (for Plan 2 branches)
│ :443 (port-forwarded)
▼
Nginx Proxy Manager (existing)
TLS termination (Let's Encrypt)
Basic auth on all proxy hosts
sub_filter rewrites S3 URLs in HTML
/_s3/ location proxies PDFs from LocalStack
│ HTTP
▼
┌─────────────────────────────────┐
│ Tolaria │
│ │
│ Orcha (Host JVM, Java 21) │
│ :8888 app │
│ :7777 admin │
│ :9999 link │
│ │
│ PostgreSQL 18 (Docker) │
│ :5432, pgvector │
│ │
│ LocalStack (Docker) │
│ :4566, S3+SQS+SSM+KMS │
│ NOT exposed to internet │
└─────────────────────────────────┘
| Domain | Forward To | SSL | Basic Auth |
|---|---|---|---|
app.demo.getorcha.com |
192.168.2.185:8888 |
Let's Encrypt | Yes |
admin.demo.getorcha.com |
192.168.2.185:7777 |
Let's Encrypt | Yes |
link.demo.getorcha.com |
192.168.2.185:9999 |
Let's Encrypt | Yes |
Three proxy hosts only — no S3 subdomain needed.
Orcha generates presigned S3 URLs pointing to http://localhost:4566 (default local-dev behavior, unchanged). NPM rewrites these URLs in HTML responses and serves PDFs through a restricted local proxy.
NPM Advanced Configuration on the app.demo.getorcha.com proxy host:
# Rewrite LocalStack presigned URLs in HTML responses
sub_filter_once off;
sub_filter_types text/html;
sub_filter 'http://localhost:4566' '/_s3';
# Serve PDFs from LocalStack — restricted to document PDF paths only
location ~ ^/_s3/v1-orcha-global-storage-[^/]+/documents/[0-9a-f-]+(/(display|email))?\.pdf$ {
limit_except GET { deny all; }
proxy_pass http://127.0.0.1:4566;
}
NPM already sets proxy_set_header Accept-Encoding "" globally, which disables upstream gzip — required for sub_filter to work. No additional config needed.
Security of the /_s3/ location:
documents/{uuid}.pdf, documents/{uuid}/display.pdf, documents/{uuid}/email.pdf)app.demo.getorcha.com (through NPM with basic auth)http://localhost:4566/v1-orcha-global-storage-local-stack/documents/{uuid}.pdf?X-Amz-...sub_filter rewrites URL in HTML response to: /_s3/v1-orcha-global-storage-local-stack/documents/{uuid}.pdf?X-Amz-...https://app.demo.getorcha.com/_s3/... (same origin — no CORS issues, basic auth cookie already present)/_s3/ location regex, proxies GET to localhost:4566Key advantage: the PDF request is same-origin (app.demo.getorcha.com/_s3/...), so the browser's basic auth credentials apply automatically — no double prompt, no unauthenticated S3 endpoint.
Two values in config.edn wrapped with #or [#env ...] for runtime override. When env vars are unset, behavior is identical to today. No S3/endpoint changes needed.
;; App base URL (OAuth callbacks, integrations)
- :com.getorcha/app-base-url #profile {:local-dev "https://orcha.barreto.tech" ...}
+ :com.getorcha/app-base-url #or [#env ORCHA_APP_BASE_URL
+ #profile {:local-dev "https://orcha.barreto.tech" ...}]
;; Link base URL (OAuth issuer, discovery)
- :com.getorcha/link-base-url #profile {:local-dev "https://link.orcha.barreto.tech" ...}
+ :com.getorcha/link-base-url #or [#env ORCHA_LINK_BASE_URL
+ #profile {:local-dev "https://link.orcha.barreto.tech" ...}]
No changes to the S3 endpoint, aws.clj, or init_aws.clj.
| Package | Purpose | Install Method |
|---|---|---|
| Docker + Docker Compose | PostgreSQL & LocalStack containers | apt |
| Java 21 (Amazon Corretto) | Host JVM for orcha | apt (Corretto repo) or SDKMAN |
| Clojure CLI | Build uberjar (clojure -X:build) |
Official installer |
| Babashka | Task runner (bb dev:up, bb dev:init-aws) |
Official installer |
Shell script (npm-setup.sh) using NPM's REST API on port 81. The API is officially supported (OpenAPI 3.1 spec), stable (the UI consumes the same API), and well-structured.
POST /api/tokens with credentials from pass show tolaria/npm-admin → JWT tokenPOST /api/nginx/access-lists with basic auth credentials from pass show tolaria/npm-basic-auth → access_list_idPOST /api/nginx/certificates × 3 → cert_idsPOST /api/nginx/proxy-hosts × 3, linking each to cert + access list. The app proxy host includes the sub_filter + /_s3/ advanced config.--teardown flag to remove all demo proxy hostspass (GPG-encrypted)# spikes/tolaria/config/demo.env
ENVIRON_NAME=local-dev
ORCHA_APP_BASE_URL=https://app.demo.getorcha.com
ORCHA_LINK_BASE_URL=https://link.demo.getorcha.com
Two env vars only. No S3 endpoint override needed.
spikes/tolaria/
├── docs/
│ ├── 2026-03-19-plan1-single-instance-design.md # This file
│ └── 2026-03-19-plan2-multi-branch-design.md
├── scripts/
│ ├── install-prereqs.sh # Install Docker, Java 21, Clojure, Babashka
│ ├── npm-setup.sh # Create/teardown NPM proxy hosts via API
│ └── run-demo.sh # Start infra, build, run orcha w/ env vars
├── config/
│ └── demo.env # Environment variables for demo instance
└── systemd/ # (Plan 2)
# One-time setup
spikes/tolaria/scripts/install-prereqs.sh
spikes/tolaria/scripts/npm-setup.sh
# Run the demo
spikes/tolaria/scripts/run-demo.sh
# Internally:
# 1. source ../spikes/tolaria/config/demo.env
# 2. cd ~/orcha/orcha && docker compose up -d
# 3. bb dev:init-aws (uses ENVIRON_NAME=local-dev, reads :endpoint from config)
# 4. clojure -X:build (if JAR doesn't exist or source changed)
# 5. java -Xmx1g -jar target/orcha.jar -m com.getorcha.system
/_s3/ location on the app proxy host/_s3/ restricted to GET requests matching document PDF path regex only — no bucket listing, no non-PDF access, no write operations/_s3/ is under app.demo.getorcha.com, so basic auth applies automatically:443 inbound from WAN; PostgreSQL (5432), LocalStack (4566), and orcha ports (8888/7777/9999) reachable from localhost onlypass (GPG-encrypted)