Skip to content

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 appportal.scalr.com, invoices.scalr.com, docs.scalr.com. Clean separation, requires wildcard cert. - Path-basedscalr.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:

  • .env file 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.