Open Questions¶
Decisions that are still open. Each question lists what depends on it, what the candidate options are, and what's blocking a decision.
This file is living. When something is decided, move it into an ADR and remove it from here.
1. Monorepo tooling¶
Question: What tooling do we use to manage workspaces, builds, and dependencies across the monorepo?
Candidates:
- pnpm workspaces + Turborepo (JS) + uv workspaces (Python). Two tools, but each is best-in-class for its language.
- Nx. One tool for both JS and Python. More opinionated, more setup.
- No tooling — just per-app package.json and pyproject.toml files. Simplest until cross-package deps grow.
Depends on: how many shared packages we end up with, whether we need affected-only CI builds.
Recommendation (tentative): start with no tooling, add pnpm workspaces + uv workspaces when shared packages are real. Skip Nx unless cross-language orchestration becomes painful.
2. Where do client deployments live?¶
Question: Should clients/ be a directory in this monorepo, or should each client have its own repo that pulls packages/ as dependencies?
Trade-offs: - In this repo: simpler dev experience, shared CI, easy to refactor across client + product. But blast radius is wider, and client-confidential code is mixed with the rest. - Separate repos: strong isolation, each client deployable independently. But shared packages must be published as real artifacts (npm/PyPI or a private registry), which adds friction.
Depends on: whether any client work is under NDA, how often client code shares logic with products.
3. Message bus¶
Question: Do we need an async message bus from day one, or start sync-only?
Candidates if needed: Redis Pub/Sub (simplest, already running for Authentik), NATS (more features), RabbitMQ (heavyweight).
Depends on: whether any near-term apps need cross-app events. If not, defer.
Note: the app.manifest.yml schema already has events_publish and events_subscribe fields. Capturing event wiring is free even before a bus exists.
4. API gateway — resolved¶
Settled in ADR 0007: Traefik v3, deployed as services/gateway/. Each app's compose carries traefik.* labels and joins the shared scalr-edge Docker network.
5. Domain strategy¶
Question: How are apps exposed publicly?
Candidates:
- Subdomain per app — portal.scalr.com, invoices.scalr.com, docs.scalr.com. Clean separation, requires wildcard cert.
- Path-based — scalr.com/invoices, scalr.com/docs. Simpler certs but cookie/CORS handling is messier.
Recommendation (tentative): subdomain per app. The app.manifest.yml schema already assumes this with the routing.subdomain field.
6. Multi-tenancy model for products¶
Question: How do subscription products isolate tenant data?
Candidates:
- Row-level — every table has a tenant_id, every query filters by it. Simplest, riskiest if you forget a filter.
- Schema-per-tenant — each tenant gets its own Postgres schema. Strong isolation, harder to query cross-tenant.
- DB-per-tenant — each tenant gets its own Postgres database. Strongest isolation, operationally heaviest.
Depends on: expected tenant count, regulatory requirements per product, whether any product needs cross-tenant analytics.
7. Versioning¶
Question: Single version for the whole monorepo, or per-app semver?
Candidates: - Single version — every push tags everything. Simple, but ties unrelated apps' release cycles together. - Per-app semver — each app has its own version. More flexible, requires release tooling.
Depends on: whether any apps will ever be released independently of the rest.
8. Secrets management¶
File shape — resolved: ADR 0008 splits each app's environment into a committed .env.app (structural, no secrets) and a single gitignored <repo>/.env.shared (the only file with real secrets). One file to sync between machines.
Still open — how does .env.shared sync? Candidates:
.envfile copied manually — simplest, but secrets sit on disk in plain text and the manual copy doesn't scale to a team.- Doppler — managed, easy CLI/SDK integration.
- Vault — self-hosted, powerful, operationally complex.
- 1Password Connect / 1Password CLI — if already using 1Password.
- SOPS + age, file committed encrypted — git-native, no vendor.
Depends on: how many secrets, how many environments, audit requirements, whether you want a vendor or not.
9. Manifest version field¶
Question: Is manifest_version: 1 overengineering for v0?
Pros of keeping: future schema migrations are easy. Cost is one line per manifest.
Pros of dropping: simpler files; can always add later.
Recommendation (tentative): keep it. Schema migrations without a version field are painful enough that the line is worth it.