Tomáš Dvořák and Devin
5eac4a382b
feat: activity feed — enriched audit events + global/project timelines ( #45 )
...
* feat(branding): white-label — public /branding, admin write, shell theming
app_settings branding.* keys (product name, logo/favicon URLs, accent
color, docs/support links). Public GET /branding so login and shell
theme pre-auth; admin writes via PUT /settings.
useBranding() applies document.title, accent CSS var and favicon;
BrandWordmark replaces hardcoded wordmarks; admin Platform settings
gain a Branding form. MCP containr_branding_get + settings_update.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(invites): team invite links — admin create/revoke, public accept
Admins mint single-use invite links (sha256-hashed token at rest,
optional email binding, configurable TTL). Public endpoints let the
accept page validate the link and register — working even while
public signup is disabled.
- migration + sqlc queries/model for user_invites
- POST /admin/invites (returns one-time token+url), GET list, DELETE revoke
- GET /auth/invites/:token public validation, POST /auth/accept-invite
- CLI containr invites list|create|revoke; MCP invites tools
- /auth/accept-invite page (expired/used/invalid states, bound-email lock)
- Admin page Team invites section: create, copy link, revoke
- OpenAPI + generated types + CLI guide
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(admin): impersonate user — 15-min token, audit-logged
POST /admin/users/:id/impersonate mints a short-lived bearer token
(impersonated_by claim) for support/debugging. Admin accounts cannot
be impersonated; every mint is audit-logged with actor + target.
- CLI: containr admin impersonate <user-id> (works via CONTAINR_TOKEN)
- MCP: containr_admin_impersonate
- Admin UI: per-user Impersonate action → token modal with usage hint
- OpenAPI + generated types
Verified live: token resolves to target, admin target 403, audit row.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(git): GitHub App manifest self-provisioning + app-level webhook
Admins create the instance GitHub App in-browser instead of hand-
setting GITHUB_APP_* env vars:
- POST /admin/git/github-app/manifest builds the manifest + URL;
the frontend auto-submits it to github.com
- /git/github-app/callback page exchanges the code via
POST /admin/git/github-app/convert; credentials persist in
app_settings (precedence over env)
- GET /admin/git/github-app reports configured/source
- POST /api/git/github-app/webhook — single app-level push receiver,
HMAC-verified, dispatches via extracted dispatchPushToServices
- install-url + installation-token paths resolve settings→env
- CLI: containr admin github-app status|manifest|convert; MCP tools
- Git providers UI shows "Set up GitHub App" for admins when
unconfigured
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: activity feed — enriched audit events, global + per-project timelines
audit_logs gains severity/category/label columns derived from
resource+action at write time; legacy rows are classified on read and
backfilled at boot in batches.
- GET /activity: global feed scoped to the caller's visible projects
(admins see all); severity/category/resource/project_id/since
filters, page+limit
- GET /projects/:id/activity: timeline correlated to the project's
services, deployments, databases and the project record; access
mirrors project read rules
- Retention worker prunes rows older than AUDIT_RETENTION_DAYS
(default 90, 0 disables), running once at boot then daily
- project.create/delete now audited so timelines aren't empty
- CLI `containr activity` with filter flags; MCP tools
containr_activity_list + containr_project_activity
- Frontend: /activity page (severity chips, icon per category) under
Operate nav; project workspace gains an Activity view tab
- OpenAPI + generated types + CLI docs
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
v0.17.0
2026-10-09 10:52:15 +02:00
Tomáš Dvořák and Devin
00e69b3733
feat(git): GitHub App manifest self-provisioning + app-level webhook ( #44 )
...
* feat(branding): white-label — public /branding, admin write, shell theming
app_settings branding.* keys (product name, logo/favicon URLs, accent
color, docs/support links). Public GET /branding so login and shell
theme pre-auth; admin writes via PUT /settings.
useBranding() applies document.title, accent CSS var and favicon;
BrandWordmark replaces hardcoded wordmarks; admin Platform settings
gain a Branding form. MCP containr_branding_get + settings_update.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(invites): team invite links — admin create/revoke, public accept
Admins mint single-use invite links (sha256-hashed token at rest,
optional email binding, configurable TTL). Public endpoints let the
accept page validate the link and register — working even while
public signup is disabled.
- migration + sqlc queries/model for user_invites
- POST /admin/invites (returns one-time token+url), GET list, DELETE revoke
- GET /auth/invites/:token public validation, POST /auth/accept-invite
- CLI containr invites list|create|revoke; MCP invites tools
- /auth/accept-invite page (expired/used/invalid states, bound-email lock)
- Admin page Team invites section: create, copy link, revoke
- OpenAPI + generated types + CLI guide
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(admin): impersonate user — 15-min token, audit-logged
POST /admin/users/:id/impersonate mints a short-lived bearer token
(impersonated_by claim) for support/debugging. Admin accounts cannot
be impersonated; every mint is audit-logged with actor + target.
- CLI: containr admin impersonate <user-id> (works via CONTAINR_TOKEN)
- MCP: containr_admin_impersonate
- Admin UI: per-user Impersonate action → token modal with usage hint
- OpenAPI + generated types
Verified live: token resolves to target, admin target 403, audit row.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(git): GitHub App manifest self-provisioning + app-level webhook
Admins create the instance GitHub App in-browser instead of hand-
setting GITHUB_APP_* env vars:
- POST /admin/git/github-app/manifest builds the manifest + URL;
the frontend auto-submits it to github.com
- /git/github-app/callback page exchanges the code via
POST /admin/git/github-app/convert; credentials persist in
app_settings (precedence over env)
- GET /admin/git/github-app reports configured/source
- POST /api/git/github-app/webhook — single app-level push receiver,
HMAC-verified, dispatches via extracted dispatchPushToServices
- install-url + installation-token paths resolve settings→env
- CLI: containr admin github-app status|manifest|convert; MCP tools
- Git providers UI shows "Set up GitHub App" for admins when
unconfigured
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
v0.16.0
2026-10-09 10:43:12 +02:00
Tomáš Dvořák and Devin
50e39fe140
feat(invites): team invite links — admin create/revoke, public accept ( #42 )
...
* feat(branding): white-label — public /branding, admin write, shell theming
app_settings branding.* keys (product name, logo/favicon URLs, accent
color, docs/support links). Public GET /branding so login and shell
theme pre-auth; admin writes via PUT /settings.
useBranding() applies document.title, accent CSS var and favicon;
BrandWordmark replaces hardcoded wordmarks; admin Platform settings
gain a Branding form. MCP containr_branding_get + settings_update.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(invites): team invite links — admin create/revoke, public accept
Admins mint single-use invite links (sha256-hashed token at rest,
optional email binding, configurable TTL). Public endpoints let the
accept page validate the link and register — working even while
public signup is disabled.
- migration + sqlc queries/model for user_invites
- POST /admin/invites (returns one-time token+url), GET list, DELETE revoke
- GET /auth/invites/:token public validation, POST /auth/accept-invite
- CLI containr invites list|create|revoke; MCP invites tools
- /auth/accept-invite page (expired/used/invalid states, bound-email lock)
- Admin page Team invites section: create, copy link, revoke
- OpenAPI + generated types + CLI guide
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
v0.15.0
2026-10-09 10:35:12 +02:00
Tomáš Dvořák and Devin
b47ced0135
feat(branding): white-label — public /branding, admin write, shell theming ( #41 )
...
app_settings branding.* keys (product name, logo/favicon URLs, accent
color, docs/support links). Public GET /branding so login and shell
theme pre-auth; admin writes via PUT /settings.
useBranding() applies document.title, accent CSS var and favicon;
BrandWordmark replaces hardcoded wordmarks; admin Platform settings
gain a Branding form. MCP containr_branding_get + settings_update.
Generated with [Devin](https://devin.ai )
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
v0.14.0
2026-10-09 10:22:58 +02:00
Tomáš Dvořák and Devin
9408464f12
feat(banners): instance announcements — admin CRUD, top bar, CLI ( #39 )
...
banners table (title/body/level/active/dismissible/schedule window).
GET /banners/active for signed-in users; admin CRUD under /admin/banners.
BannerBar renders above the header with per-banner dismissal
(localStorage); admin console gains an Announcements section;
`containr banners` manages them and `auth status` prints active ones.
Generated with [Devin](https://devin.ai )
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
v0.13.0
2026-10-09 10:10:18 +02:00
Tomáš Dvořák
954a1d1f80
Merge pull request #38 from Dvorinka/feat/operations-page
...
feat(ops): operations page — live jobs, queue depth, failures
v0.12.0
2026-10-09 09:57:22 +02:00
Tomáš Dvořák
cf7afd2ba7
Merge pull request #37 from Dvorinka/feat/outbound-webhooks
...
feat: outbound webhooks — signed event delivery
2026-10-09 09:56:45 +02:00
Tomas Dvorak and Devin
f524d95894
feat(ops): /operations endpoint + page — live jobs, queue depth, failures
...
GET /api/v1/operations aggregates active deployments, per-service
deploy-queue depth via Queue.Snapshot(), 24h deployment failures,
cron runs and backups scoped to the caller's projects (admins see all).
Frontend /operations page (Operate nav) shows in-flight count, queued
depth, failures and backup stats with live refetch while deploys are
active. CLI `containr operations` + MCP containr_operations_get.
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-09 09:51:38 +02:00
Tomas Dvorak
f499678964
feat: outbound webhooks — signed event delivery + delivery log
...
Every audited action now emits a "resource.action" event (bare actions
get resource-qualified; already-qualified actions like service.clone
pass through). Webhooks subscribe per-user with exact names, resource.*
wildcards, or *.
- schema: outbound_webhooks + webhook_deliveries (sqlc + goose)
- dispatcher: 4 workers, 512-queue, 3 attempts with backoff, 10s timeout;
deliveries record status/http-code/body(4KiB)/duration
- signing: X-Containr-Signature sha256=<hmac> over raw body;
X-Containr-Event + X-Containr-Delivery headers; custom headers per hook
- api: CRUD + /deliveries + /test (ping) under authed group
- fix: SetAuditDB never called — LogAudit was a silent no-op at every
call site; now wired at boot
- coverage: service create/delete audited (previously uninstrumented)
- cli: containr webhooks list|create|update|delete|deliveries|test
- mcp: 6 tools, delete requires confirm
- frontend: /settings/webhooks page — list/create/expand-deliveries/
test/disable/delete, secret-shown-once banner; nav + account menu
- openapi + regenerated types; CLI.md + docs snapshot; roadmap ticks
2026-10-09 09:44:08 +02:00
Tomáš Dvořák
64cbf82dd8
Merge pull request #36 from Dvorinka/feat/compose-import-project
...
feat: direct docker-compose import into projects
v0.11.0
2026-10-09 09:43:15 +02:00
Tomas Dvorak
5c2f2861bd
feat: direct docker-compose import into projects
...
POST /projects/:id/import-compose deploys a compose file straight into a
project — no template row required. Well-known DB images become managed
databases attached to the project network under their service name as DNS
alias; other services deploy in dependency order via deployTemplateGraph.
- api: extract composeToSpecs converter (shared by the template
import-compose preview and the new project endpoint); fix dropped env
warnings (pointer receiver); replace insertion sort with sort.Strings
- cascade: database_services.project_id links graph-provisioned DBs to
their project so project delete tears down runtime + row (previously
leaked container + network)
- cli: new `containr import` group, `import compose <project> <file|->`
- mcp: containr_projects_import_compose tool
- openapi + regenerated frontend types; docs snapshot + CLI.md
- frontend: compose import modal on the project workspace toolbar
- tests: composeToSpecs conversion, ordering, db-image map, warnings
2026-10-09 09:33:38 +02:00
Tomáš Dvořák
dca8aaa7b3
Merge pull request #35 from Dvorinka/fix/mobile-auth-gate
...
fix(mobile): gate authed routes — cold-start redbox fix
2026-10-09 05:49:29 +02:00
Tomas Dvorak and Devin
6946357a7b
fix(mobile): gate authed routes behind Stack.Protected — cold-start redbox
...
'/' redirected to /(tabs) unconditionally; tab screens call useApi()
which throws 'not signed in' during render — before AuthGate's effect
redirect could run. Sign-out hit the same throw.
- index redirects based on session state (api ? tabs : login)
- authed screens wrapped in Stack.Protected guard={!!api}
- found by HP-WIN11 emulator visual pass
Generated with [Devin](https://devin.ai )
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-09 05:45:44 +02:00
Tomáš Dvořák
a0952faf25
Merge pull request #31 from Dvorinka/feat/template-catalog
...
feat(templates): catalog expansion — 5 new managed-DB stacks
v0.10.0
2026-10-09 05:30:52 +02:00
Tomas Dvorak
999d719377
Merge remote-tracking branch 'origin/main' into feat/template-catalog
2026-10-09 05:30:27 +02:00
Tomáš Dvořák
8d682583ff
Merge pull request #30 from Dvorinka/feat/env-doctor
...
feat(services): env doctor — empty vars, bad refs, unreadable secrets
2026-10-09 05:30:22 +02:00
Tomas Dvorak
c80fbc766d
Merge remote-tracking branch 'origin/main' into feat/env-doctor
2026-10-09 05:29:58 +02:00
Tomáš Dvořák
99ee4e79cf
Merge pull request #29 from Dvorinka/feat/sleep-mode
...
feat(services): sleep mode — scale-to-zero on idle, wake on traffic
2026-10-09 05:29:53 +02:00
Tomas Dvorak
f729ef2c82
Merge remote-tracking branch 'origin/main' into feat/sleep-mode
2026-10-09 05:29:29 +02:00
Tomáš Dvořák
f90fc5f5ad
Merge pull request #28 from Dvorinka/feat/templates-v2
...
feat(templates): v2 — multi-service graph templates, expressions, compose import
2026-10-09 05:29:24 +02:00
Tomas Dvorak
f68225a97d
Merge remote-tracking branch 'origin/main' into feat/templates-v2
2026-10-09 05:29:01 +02:00
Tomáš Dvořák
7536443610
Merge pull request #27 from Dvorinka/feat/backup-import
...
feat(databases): backup import — upload .tar.gz as a restore point
2026-10-09 05:28:55 +02:00
Tomas Dvorak
68da4236fa
Merge remote-tracking branch 'origin/main' into feat/backup-import
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:28:31 +02:00
Tomáš Dvořák
b9441f3e11
Merge pull request #26 from Dvorinka/feat/backup-targets
...
feat(databases): S3-compatible backup targets — offsite archives + remote restore
2026-10-09 05:28:26 +02:00
Tomas Dvorak
605a43455d
Merge remote-tracking branch 'origin/main' into feat/backup-targets
2026-10-09 05:28:03 +02:00
Tomáš Dvořák
c7256954c2
Merge pull request #25 from Dvorinka/feat/db-public-port
...
feat: public_port for managed databases — opt-in 0.0.0.0 bind
2026-10-09 05:27:00 +02:00
Tomas Dvorak
a50260e857
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:09 +02:00
Tomas Dvorak
3441acbc37
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:09 +02:00
Tomas Dvorak
c5e0561add
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:08 +02:00
Tomas Dvorak
1ef5ea5f7f
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:08 +02:00
Tomas Dvorak
d26452d97e
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:08 +02:00
Tomas Dvorak
164a39c9c6
fix: resolve ROADMAP2 merge markers
2026-10-09 05:21:08 +02:00
Tomas Dvorak
63f65c78cc
Merge remote-tracking branch 'origin/main' into feat/template-catalog
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:47 +02:00
Tomas Dvorak
d5960fdfff
Merge remote-tracking branch 'origin/main' into feat/env-doctor
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:47 +02:00
Tomas Dvorak
31c04ad51d
Merge remote-tracking branch 'origin/main' into feat/sleep-mode
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:46 +02:00
Tomas Dvorak
03f6dcc80d
Merge remote-tracking branch 'origin/main' into feat/templates-v2
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:46 +02:00
Tomas Dvorak
1a4ee99f5b
Merge remote-tracking branch 'origin/main' into feat/backup-import
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:46 +02:00
Tomas Dvorak
264f619be5
Merge remote-tracking branch 'origin/main' into feat/backup-targets
...
# Conflicts:
# ROADMAP2.md
2026-10-09 05:20:46 +02:00
Tomas Dvorak
11525194b0
Merge remote-tracking branch 'origin/main' into feat/db-public-port
2026-10-09 05:20:45 +02:00
Tomáš Dvořák
9e3994e937
Merge pull request #34 from Dvorinka/feat/mobile-app
...
feat(mobile): Containr companion app (Expo)
v0.9.0
2026-10-09 05:20:17 +02:00
Tomáš Dvořák
355eb2c9ab
Merge pull request #24 from Dvorinka/feat/external-databases
...
feat: external databases — register, probe, encrypted credentials
2026-10-09 05:19:31 +02:00
Tomas Dvorak
b5a1a8cc21
Merge remote-tracking branch 'origin/main' into feat/template-catalog
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:38 +02:00
Tomas Dvorak
91f40a652d
Merge remote-tracking branch 'origin/main' into feat/env-doctor
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:38 +02:00
Tomas Dvorak
2be1715015
Merge remote-tracking branch 'origin/main' into feat/mobile-app
2026-10-09 05:12:38 +02:00
Tomas Dvorak
b32e63368a
Merge remote-tracking branch 'origin/main' into feat/sleep-mode
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:37 +02:00
Tomas Dvorak
f1a0f2189d
Merge remote-tracking branch 'origin/main' into feat/templates-v2
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:37 +02:00
Tomas Dvorak
e3676bd89a
Merge remote-tracking branch 'origin/main' into feat/backup-import
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:37 +02:00
Tomas Dvorak
71c7d2b9f0
Merge remote-tracking branch 'origin/main' into feat/backup-targets
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:12:19 +02:00
Tomas Dvorak
e67a39436c
Merge remote-tracking branch 'origin/main' into feat/external-databases
2026-10-09 05:12:01 +02:00
Tomas Dvorak
2c3d9fc123
Merge remote-tracking branch 'origin/main' into feat/db-public-port
...
# Conflicts:
# docs/guides/CLI.md
2026-10-09 05:11:56 +02:00