An app-level grant (user_app_perms/role_app_perms) alone used to be
enough to show an app's portal tile and pass AuthGate, even with zero
capabilities held. Most apps only gate specific routes/buttons with
capabilities, not their dashboard/shell, so a role with the app toggled
on but no caps granted (e.g. wages, utilities, hvac, plant,
tablet-checkin — none of which auto-grant Staff caps) still saw the
full app. Apps with no capabilities defined at all keep prior behaviour.
Several 'grant Staff these default caps' seeds in initDb() re-ran on every
auth container restart, guarded by 'role currently has no caps for this
app' as a proxy for 'first deploy'. If an admin revoked all of Staff's
cashup capabilities in the portal, the next restart saw zero cashup caps
and silently re-granted all 7 non-settings caps back — looking like
permissions randomly resetting to full access.
Add a seed_flags table + seedOnce() helper so these default-cap grants
(cashup x2, hk-planner, kds, tasks) run exactly once ever, tracked by a
durable flag instead of re-derived from current state. Deliberate
permission changes now stick across restarts/redeploys.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
LXC 131, base_path '/weather/admin' (portal card opens the authed status
page directly; the app's bare root '/weather/' is the public, no-auth
reception display). Single 'settings' capability, not auto-granted to
Staff — grant via Admin -> Roles.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
KDS was seeded with the same theme_color as kitchen (#0d9488) — the two
Kitchen-category portal tiles were indistinguishable. Changed to #ea580c
(orange). See kitchen-port-log.md E17.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a third capability 'edit' (Edit Directors Forecast) for the reports
app to gate pickup/dry/wet override saves and snapshot creation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Used by the settings UI so operators can securely copy the shared session secret
into Hosted Tables without ever exposing it in logs or config files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Seeds restaurant_bookings as inactive (active=false) so it stays hidden until the
settings service configures the Hosted Tables URL. Adds PATCH /api/auth/internal/apps/:slug
so settings can update base_path and active without auth restarts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
history is a portal alias for /forecasting/history, not its own service.
Setting internal_host=NULL prevents spurious health-check failures and
deploy attempts against /opt/history on LXC 113.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a 'history' app slug that deep-links to the forecasting app's
History page, allowing it to appear as a standalone tile in the portal.
Admins grant it alongside the 'forecasting' app via the Roles UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Settings service calls POST /api/auth/internal/cache-bust (Bearer SETTINGS_SECRET)
after saving workforce or smtp integrations, so location/credential changes take
effect immediately without waiting for the 5-minute TTL or restarting the container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If a bearer_token is stored in settings, getWorkforceToken() returns it
immediately without doing an OAuth password grant. Validation accepts
either bearer_token or email+password.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /api/auth/users?app=<slug> — non-admin user list for task allocation
(requester must have access to the app; returns active users with access,
minimal fields). Seed row for the maintenance app (LXC 121).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Splits the old 'reports' cap into reports + cash_summary,
'floats' into floats + safe_count, and adds a new 'history' cap.
Existing installs get the three new caps added to the Staff role
via an additive per-cap seed so admin-managed configs are unaffected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Schema:
- app_capabilities: capabilities each app exposes (<app>:<slug>)
- role_capabilities / user_capabilities: grants via roles and direct
- Seed cashup caps (count, finalise, reports, floats, settings)
- Non-breaking migration: default Staff role gets all cashup caps
except settings (previously only is_admin reached settings)
Resolution:
- getUserCapabilities(): admins get all; others get union of
role + direct grants, as "<app>:<cap>" strings
- caps[] added to JWT payload (login + register)
- /verify returns live capabilities for the requested app (bare slugs)
Admin API:
- GET /admin/capabilities catalogue
- grant/revoke capability on roles and users
- roles/users GET responses now include their capabilities
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>