166 lines
7.9 KiB
Markdown
166 lines
7.9 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 # driver, api_key, sender_address, sender_name
|
|
├── infrastructure/
|
|
│ └── nats/auth # token; read by NATS and notifications
|
|
└── bootstrap/
|
|
└── mongo # 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 |
|
|
| --- | --- |
|
|
| `driver` | `resend` |
|
|
| `api_key` | Your Resend API key |
|
|
| `sender_address` | Your verified sender address |
|
|
| `sender_name` | Your display name |
|
|
|
|
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 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 email
|
|
settings causes Agent to restart notifications. Existing values survive bootstrap.
|
|
|
|
## 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. 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.
|