Appearance
Architecture
Finluth is a modular monolith.
apps/webis the React dashboard.apps/serveris the Fastify API, health loop, and deployment runner. In production it also serves the built dashboard.packages/dbis the Drizzle schema and SQL migrations.packages/sharedis validation and organization roles.
Modules inside the server:
- Identity: sessions, invites, roles
- Catalog: projects, environments, services, secrets
- Delivery: manual records and an SSH provider behind an allowlisted command
- Observe: HTTP health checks, a host heartbeat for CPU, memory, and disk, and one chat webhook for down, still down, recovery, and a failed deploy
The first organization is created at boot. The first project is inserted from deploy/seed.json. Application code does not special-case any project.
Request path
finluth.com shows the public page. finluth.com/status shows production health, with no deploy detail. app.finluth.com is the signed-in dashboard and /api. Both hostnames hit the same process. Caddy terminates TLS.
Deployments
A service has a deployment target. manual stores a record. ssh connects with an encrypted key, runs an allowlisted command in an allowlisted directory, then checks the health URL. Production SSH deploys require the actor to have TOTP enabled. A GitHub push deploys production only when that project’s webhook option is on; the webhook secret is the credential. An owner can schedule that same kind of command hourly, daily, or weekly. The first run waits for one full interval, and a production schedule requires two-factor on the account that saved it.
apps/cli is the finluth command. It calls this API with a bearer token from Settings. finluth status prints each service. finluth deploy <project> <service> queues that service’s SSH target. finluth logs <project> <service> pulls the last lines. It uses app.finluth.com. Production deploys still require two-factor on the account that owns the token.
Data
Tenant rows carry organization_id. Secrets and SSH keys are AES-256-GCM ciphertext. The master key lives in APP_MASTER_KEY, not in the database. Audit rows are append-only from the application. There is no API that updates or deletes them.
Later
A second project is another row. A second deploy provider is another class behind the same deployment table. If deploys need to leave the request process, run the delivery module as a second entrypoint that reads status = queued. Do not add a queue broker before that is necessary.