183 lines
9.3 KiB
Markdown
183 lines
9.3 KiB
Markdown
# Local development
|
|
|
|
Task [#42](https://git.sendico.io/tech/sxchnge/issues/42), branch `dev-env-42`.
|
|
|
|
## Build and bootstrap
|
|
|
|
Requires Docker with Compose v2 and BuildKit, Python 3.10+ and Git. Docker builds
|
|
Go, protobuf bindings and Flutter web; host Go, protoc and Flutter installations
|
|
are not required for `make build && make up`. The first Flutter SDK download can
|
|
take several minutes. Both ARM64 and AMD64 build from the same source definitions.
|
|
|
|
```sh
|
|
make build && make up
|
|
```
|
|
|
|
`make build` resolves the current stable upstream releases, pulls infrastructure
|
|
and builds NATS packaging, notifications, BFF and frontend images. Vault and Mongo
|
|
use upstream images directly. Resolution excludes prereleases and selects Nginx's
|
|
stable channel. The exact versions and manifest digests are saved in the ignored
|
|
`.local/dev/images.env`; Flutter's stable release is pinned by Git commit. Build
|
|
fails if upstream metadata cannot be resolved. Run `make build` again to refresh
|
|
versions. Review database release upgrade requirements before updating a populated
|
|
local volume across major versions; the tooling does not perform data migrations.
|
|
|
|
`make up` starts persistent Vault, initializes/unseals it, provisions secrets and
|
|
AppRoles, initializes an authenticated single-member MongoDB replica set, configures
|
|
Vault's MongoDB database plugin, and starts authenticated NATS JetStream. It then
|
|
starts notifications, BFF and the frontend when the required email settings exist.
|
|
|
|
**On the first run, `make up` intentionally exits with an error after infrastructure
|
|
bootstrap because Resend credentials have not been supplied.** No placeholder API
|
|
key, mock delivery mode or embedded credential is installed. The three infrastructure
|
|
services remain running so you can configure Vault, then rerun `make up`.
|
|
|
|
## Configure Resend in Vault
|
|
|
|
The default email driver is `resend`. In the Vault UI at
|
|
[http://127.0.0.1:18200](http://127.0.0.1:18200), edit the KV v2 secret **`kv/email`**
|
|
(API path `kv/data/email`):
|
|
|
|
| Field | Value |
|
|
| --- | --- |
|
|
| `driver` | `resend` |
|
|
| `api_key` | Your Resend API key |
|
|
| `sender_address` | Your verified Resend sender email |
|
|
| `sender_name` | `SXCHNGE` or your desired display name |
|
|
|
|
Local administrative login material is in the private, ignored
|
|
`.local/dev/admin/init.json` (`root_token`). Keep that file private. Product
|
|
containers never mount it. You can also provision the same secret with a hidden
|
|
terminal prompt, without placing the API key in shell history:
|
|
|
|
```sh
|
|
python3 dev/local.py email
|
|
make up
|
|
```
|
|
|
|
The startup check rejects an empty key or the bootstrap sender placeholder. It
|
|
does not validate the key against Resend or prove domain verification; an invalid
|
|
key fails at delivery. Vault Agent fetches the key and email settings and injects
|
|
them into the notifications process. There is no `.env` secret fallback. Changing
|
|
`kv/email` restarts the notification process automatically within about 10 seconds.
|
|
Bootstrapping again preserves your existing email secret.
|
|
|
|
## Open the product
|
|
|
|
After providing Resend credentials:
|
|
|
|
- Frontend and proxied API: [https://localhost:5126](https://localhost:5126).
|
|
- BFF health: [https://localhost:5126/api/v1/health](https://localhost:5126/api/v1/health).
|
|
- Vault UI/API: [http://127.0.0.1:18200](http://127.0.0.1:18200).
|
|
|
|
Trust `.local/dev/ca.pem` in your local browser/OS certificate store to use HTTPS
|
|
without a certificate error. Trust installation is a separate local action, not
|
|
performed by the bootstrap. For a command-line check without changing trust:
|
|
|
|
```sh
|
|
curl --cacert .local/dev/ca.pem https://localhost:5126/api/v1/health
|
|
```
|
|
|
|
HTTPS keeps the existing Secure/HttpOnly refresh cookie contract. Nginx serves
|
|
Flutter and proxies `/api/` to the BFF on the same origin. The bundle selects the
|
|
existing backend auth adapter with the public client ID `sxchnge-local-web`.
|
|
Bootstrap inserts this public browser client if absent; it has no client secret.
|
|
No frontend widget, service or provider architecture changes are needed.
|
|
|
|
The rest of the app retains its existing capabilities and limitations: wallet,
|
|
KYC and settings fixtures are not turned into production integrations by this
|
|
container setup. Full email signup/login requires a working Resend account.
|
|
|
|
## Secret and connection lifecycle
|
|
|
|
| Consumer | Vault access |
|
|
| --- | --- |
|
|
| BFF | `database/creds/bff`, JWT key, notification caller token and CA |
|
|
| Notifications | `database/creds/notifications`, NATS token, caller token, TLS certificate/key and email settings |
|
|
| NATS | NATS authentication token only |
|
|
| Bootstrap | Infrastructure administration, PKI and initial identities |
|
|
|
|
The database engine is configured at `database/config/mongo` with
|
|
`mongodb-database-plugin`. It uses its own `vault-manager` Mongo user, whose
|
|
password is rotated by Vault immediately after configuration. The manager has
|
|
`userAdminAnyDatabase`; applications never receive that credential. The independent
|
|
local Mongo administrator and replica-set key come from Vault's random generator
|
|
and are retained in `kv/bootstrap/mongo` for local provisioning.
|
|
|
|
The BFF role has `readWrite` only on `sxchnge`; notifications has `readWrite` only
|
|
on `sxchnge_notifications`. Mongo URIs come from dynamic Vault credentials, not
|
|
static application users. Leases default to one hour with a 24-hour maximum.
|
|
Vault Agent renews leases, obtains replacement credentials, and restarts its child
|
|
process when credentials change. Apps retain their existing shared database and
|
|
messaging connection wrappers. No new direct application database path is added.
|
|
|
|
Each agent authenticates using its own AppRole with a renewable periodic token.
|
|
Local SecretIDs are reusable for container restarts; each container mounts only
|
|
its own identity directory under `.local/dev/agents/`. Revoking an AppRole SecretID
|
|
requires removing its matching local `secret-id` file and rerunning `make up` to
|
|
issue a replacement. Revoking an active token/lease terminates its credentials;
|
|
Agent's retries and the container restart policy recover when Vault is reachable.
|
|
|
|
The notification gRPC connection uses a Vault-issued certificate for
|
|
`notifications` and the BFF validates the Vault CA and hostname. Frontend HTTPS
|
|
certificates also come from Vault PKI. Leaf certificates last 30 days; `make up`
|
|
refreshes them when less than seven days remain and reloads Nginx. Run it regularly
|
|
for a development stack kept online for weeks. Agents restart when their TLS KV
|
|
secret changes. Application secrets are never put into the frontend bundle.
|
|
|
|
Vault, Mongo and JetStream data use project-scoped named Docker volumes. Local
|
|
Vault unseal material, administrator login, per-service bootstrap identities and
|
|
frontend TLS material are under ignored `.local/dev/`, outside image build contexts.
|
|
The local Vault automatically unseals using its local key on container restart.
|
|
These convenience credentials and loopback HTTP Vault access are for this local
|
|
machine; this is not a production Vault deployment. Do not share `.local/dev/`.
|
|
Keep it paired with its matching Docker volumes for recovery.
|
|
|
|
Mongo and NATS expose no host ports. The `sxchnge-local` network, volumes and
|
|
container names are independent of existing Sendico stacks. Only the frontend
|
|
and Vault bind host loopback ports. No Sendico database, message bus or secrets
|
|
are consumed.
|
|
|
|
## Manage and verify
|
|
|
|
```sh
|
|
make ps
|
|
make logs
|
|
make down # preserves named volumes and local bootstrap material
|
|
make up # idempotent provisioning and readiness checks
|
|
make test-dev-infra # works before Resend provisioning
|
|
make test-dev # requires all six services running
|
|
```
|
|
|
|
The infrastructure check exercises AppRole access restrictions, real Vault
|
|
MongoDB credential issuance, per-database access, lease renewal and user deletion
|
|
on revocation. The complete check also verifies service health and frontend/API
|
|
HTTPS with the generated CA. These checks never send a real email.
|
|
|
|
Backend regression tests use fresh `mongo:latest` and `nats:latest` official
|
|
stable images, pulled before `make test-backend`. Normal validation remains
|
|
`make test` and `make lint-backend` and requires the host SDKs described in the
|
|
repository guides.
|
|
|
|
Deleting Docker volumes removes local data; `make down` deliberately keeps them.
|
|
If starting from scratch, stop the stack and explicitly remove this project's
|
|
three volumes **and** its matching `.local/dev/` bootstrap state together. Never
|
|
reuse old unseal material with a newly initialized Vault volume, or retain Mongo
|
|
users while discarding the Vault data that manages their leases.
|
|
|
|
## Sources and checked releases
|
|
|
|
On 2026-09-12 the resolver selected Vault 2.1.0, MongoDB 8.3.9, NATS 2.14.6,
|
|
Go 1.27.1, Nginx stable 1.30.4, Ubuntu 26.04 and Flutter stable 3.47.4. These are
|
|
verification records, not fixed future defaults. Release selection follows the
|
|
[Vault releases](https://releases.hashicorp.com/vault/),
|
|
[MongoDB stable release notes](https://www.mongodb.com/docs/manual/release-notes/),
|
|
[NATS releases](https://github.com/nats-io/nats-server/releases/latest),
|
|
[Nginx downloads](https://nginx.org/en/download.html),
|
|
[Go releases](https://go.dev/dl/) and
|
|
[Flutter stable channel](https://github.com/flutter/flutter/tree/stable).
|
|
The implementation follows HashiCorp's
|
|
[MongoDB database plugin](https://developer.hashicorp.com/vault/docs/secrets/databases/mongodb)
|
|
and [Agent process supervisor](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/process-supervisor)
|
|
contracts.
|