181 lines
8.5 KiB
Markdown
181 lines
8.5 KiB
Markdown
# Local development
|
|
|
|
Task [#42](https://git.sendico.io/tech/sxchnge/issues/42),
|
|
[PR #43](https://git.sendico.io/tech/sxchnge/pulls/43), branch `dev-env-42`.
|
|
|
|
## Build and start
|
|
|
|
Requires Make, a POSIX shell and a local Docker engine with Compose v2 and
|
|
BuildKit. No host Python, Go, Flutter, jq or curl is required. Make runs bootstrap
|
|
tooling in Docker with the local Docker socket and workspace mounted. Remote
|
|
Docker engines are unsupported.
|
|
|
|
```sh
|
|
make build && make up
|
|
```
|
|
|
|
The build resolves the latest stable releases and pins their digests in ignored
|
|
`.local/dev/images.env`. Prereleases are excluded, resolution failures stop the
|
|
build, and previous pins are retained on failure. Flutter is pinned to the stable
|
|
release commit. Tooling uses a freshly pulled stable Docker CLI digest. Generated
|
|
protobuf bindings and Flutter output are built locally/in Docker and never tracked.
|
|
|
|
Compose is at `infra/local/compose.yml`; Make supplies the repository root as its
|
|
project directory. Dockerfiles are in `infra/images/`. Runtime and startup files
|
|
are grouped by service under `infra/local/`. The stack consists of Vault, Mongo,
|
|
NATS, notifications, BFF and Caddy serving the frontend.
|
|
|
|
On first startup, bootstrap initializes/unseals persistent Vault, provisions
|
|
AppRoles and secrets, initializes Mongo replica set `rs0`, configures Vault's Mongo
|
|
plugin, and starts authenticated NATS JetStream. Product startup fails explicitly
|
|
until Resend credentials exist. Infrastructure remains available for provisioning.
|
|
|
|
## Vault layout
|
|
|
|
Paths below are inside the KV v2 mount `kv`; the API inserts `data/` after `kv/`.
|
|
|
|
```text
|
|
kv/
|
|
├── services/
|
|
│ ├── bff/jwt # jwt
|
|
│ └── notifications/
|
|
│ ├── callers/bff # token; read by BFF and notifications
|
|
│ └── email/resend # api_key only
|
|
└── infrastructure/
|
|
├── nats/auth # token; read by NATS and notifications
|
|
└── mongo/bootstrap # local administrator and replica-set bootstrap material
|
|
```
|
|
|
|
Every secret has an owning service or infrastructure component. Service policies
|
|
permit only the exact paths they consume. Applications cannot read bootstrap
|
|
credentials. Mongo application users are dynamic secrets under
|
|
`database/creds/bff` and `database/creds/notifications`, not KV entries. They have
|
|
`readWrite` only on their respective `sxchnge` / `sxchnge_notifications` databases.
|
|
Vault uses `mongodb-database-plugin` and its own `vault-manager` account, whose
|
|
password is rotated immediately after initial configuration.
|
|
|
|
There are no frontend certificates, notification certificates or generic `tls`
|
|
entries in KV. Browser certificates belong to Caddy; internal gRPC is plaintext
|
|
on the private Docker network and retains caller-token authentication.
|
|
|
|
## Resend
|
|
|
|
Open [Vault](http://127.0.0.1:18200) and edit
|
|
`kv/services/notifications/email/resend`:
|
|
|
|
| Field | Value |
|
|
| --- | --- |
|
|
| `api_key` | Your Resend API key |
|
|
|
|
The driver and sender settings belong in `infra/local/notifications/runtime.yml`:
|
|
|
|
```yaml
|
|
email:
|
|
driver: resend
|
|
settings:
|
|
api_key_env: EMAIL_API_KEY
|
|
sender_address: "your-verified-sender@example.com"
|
|
sender_name: "SXCHNGE"
|
|
```
|
|
|
|
Set your verified address in that file and rebuild. It is intentionally empty by
|
|
default, so notifications cannot start with an unconfigured sender. Vault Agent
|
|
injects only the API key; it does not generate email configuration.
|
|
|
|
Local administrator login material is in private `.local/dev/admin/init.json`.
|
|
Product containers cannot access it. Alternatively use the hidden terminal prompt:
|
|
|
|
```sh
|
|
make email
|
|
make build && make up
|
|
```
|
|
|
|
No placeholder key or delivery fallback is installed. Startup validates presence;
|
|
it does not send an email or validate domain ownership with Resend. Updating the
|
|
API key causes Agent to restart notifications. Driver/sender changes require an
|
|
image rebuild. Existing API keys survive bootstrap. Migration removes legacy
|
|
driver/sender fields from the current Vault secret with a version-checked write;
|
|
the preceding version is preserved in Vault history and the private archive.
|
|
|
|
## Browser HTTPS and internal connections
|
|
|
|
Caddy serves Flutter files and proxies `/api/*` to BFF over HTTP. Open
|
|
[https://localhost:5126](https://localhost:5126). HTTPS preserves Secure/HttpOnly
|
|
cookies and same-origin API access. Caddy uses `tls internal` and manages its local
|
|
CA, certificates and renewal in the `caddy-data` volume. The `caddy-config` volume
|
|
stores Caddy runtime configuration. Neither volume nor Caddy has Vault access.
|
|
|
|
After frontend starts, Make exports only the public Caddy CA to `.local/dev/ca.pem`.
|
|
Trust it in your browser/OS to avoid certificate errors. This is a new CA when
|
|
migrating from Vault-issued browser certificates; the old CA will not trust it.
|
|
Trust installation is not automated. A CLI check can use:
|
|
|
|
```sh
|
|
curl --cacert .local/dev/ca.pem https://localhost:5126/api/v1/health
|
|
```
|
|
|
|
BFF calls notifications over plaintext gRPC with its caller token. Mongo and NATS
|
|
also stay within the private Compose network; no internal service publishes a host
|
|
port. Only frontend and Vault bind host loopback ports. TLS remains optional in
|
|
the reusable notification client/server for deployments that explicitly configure
|
|
it; local runtime configuration contains no certificate settings.
|
|
|
|
Caddy behavior follows its [internal TLS](https://caddyserver.com/docs/caddyfile/directives/tls)
|
|
and [automatic HTTPS](https://caddyserver.com/docs/automatic-https) documentation.
|
|
|
|
## Mongo transactions and credential rotation
|
|
|
|
Mongo is an authenticated single-node replica set (`rs0`), supporting multi-document
|
|
transactions. It has no failover or redundant data copy. A Mongo restart interrupts
|
|
access even though application credential replacement does not.
|
|
|
|
The shared database wrapper authenticates with each service's AppRole and obtains
|
|
an independent token/lease for each pool. At half the shorter token/lease lifetime,
|
|
it verifies a replacement pool before publishing it. Active operations, cursors
|
|
and full transactions retain the old pool until they finish, then it closes and
|
|
its token is revoked. Nested stores and commit retries keep the same pool.
|
|
|
|
Failed refreshes retain valid credentials and retry with bounded backoff. After
|
|
expiry, database operations fail closed until automatic recovery. Administrative
|
|
revocation, infrastructure outages and operations longer than their lease can
|
|
still interrupt work. Repository consumers use `source.Source` operation scopes;
|
|
`source.Fixed` adapts externally owned static/test connections.
|
|
|
|
## Migration and persistence
|
|
|
|
Bootstrap migrates the old flat `bff`, `caller`, `email` and `nats` paths into the
|
|
hierarchy above, and moves `bootstrap/mongo` to `infrastructure/mongo/bootstrap`.
|
|
It copies with CAS protection, compares values, and refuses to
|
|
overwrite conflicting destinations. After consumers use the new paths, it removes
|
|
old KV metadata and obsolete `tls`, `frontend-tls`, `notifications-tls` entries.
|
|
Readable old version history and metadata are archived privately in
|
|
`.local/dev/migrations/organized-v1/` before removal. That archive is administrative
|
|
recovery material; applications do not mount it. Old deleted/destroyed versions
|
|
are not recreated. The old PKI engine is not used or provisioned by this stack;
|
|
an existing PKI mount is retained to avoid destroying its non-exportable signing
|
|
key and history as part of a KV reorganization.
|
|
|
|
Vault, Mongo, JetStream and Caddy data use project-scoped named volumes. Private
|
|
bootstrap material lives in `.local/dev/`. `make down` preserves both. Vault's local
|
|
unseal file allows automatic unsealing on restart. This local convenience and
|
|
loopback HTTP Vault endpoint are for development. Keep bootstrap files paired
|
|
with their matching volumes; do not discard one side independently.
|
|
|
|
## Verification
|
|
|
|
```sh
|
|
make ps
|
|
make logs
|
|
make down
|
|
make test-dev-tooling # shell analysis, failure paths, migration conflict checks
|
|
make test-dev-infra # policy isolation, real Mongo transactions and lease lifecycle
|
|
make test-dev-rotation # temporarily deploy BFF/frontend and exercise short leases
|
|
make test-dev # requires all six services; verified frontend/API HTTPS
|
|
```
|
|
|
|
Rotation validation temporarily shortens the BFF role TTL, recreates BFF/frontend,
|
|
and requires three replacements with unchanged BFF PID and zero unexpected
|
|
transaction-backed HTTP responses. It restores TTLs and removes services it
|
|
started. No verification command sends email. Backend validation remains
|
|
`make test` and `make lint-backend`, using the host SDKs documented in module guides.
|