The cron backend was a stub: calculateNextRun returned now+1h for any
input and executeCronJob slept 2s then wrote "success" without running
the command. Now robfig/cron parses and validates schedules (400 on
invalid), a 60s scheduler goroutine executes due enabled jobs, and
commands run via docker exec sh -c inside the service's container with
stdout+stderr captured into cron_executions. Added Client.ExecRun for
attached output capture. UI: Cron tab on the service detail page with
create/edit/enable-toggle, manual Run, and execution history with logs.
Verified e2e against real Postgres + Docker (trigger and scheduler tick
both produce real captured output).
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Per-node onboarding tokens replace the placeholder <token> in the agent
connect command: raw cagt_ tokens are returned once at issuance, only
sha256 hashes persist in agent_auth_tokens, and listing exposes metadata
only. Public agent routes accept env-configured shared tokens or any
active DB-issued token; successful validation stamps last_used_at and
revoked tokens are rejected — verified end-to-end against a real stack
(register, heartbeat, revoke → 401, bogus → 401).
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The audit trail existed only as a summary card — no table, no filters.
The list endpoint was also self-scoped and never returned user_email
(the field existed on the struct but was never selected).
Backend: GET /audit-logs now joins users for email and accepts
user_id / actor (email ILIKE) / since (RFC3339) filters alongside the
existing resource/action/page/limit. Widening past self-scope matches
the platform's no-roles trust model — every authed user can already
create users. RLS on audit_logs is dormant (superuser connection,
app.current_user_id never set).
Frontend: /settings/audit-logs page with deferred filter inputs, time
range select, paginated table (time, actor, resource, action, id, IP,
details), linked from a Settings card. Spec updated and types
regenerated.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
PUT /services/{id}/variables replaced every row, and GET masks secrets as
******** — echoing the fetched list back would have overwritten real
secrets with asterisks. The backend now keeps the stored value when a
submitted secret still carries the mask.
Service detail gains a Variables section: add/edit/remove rows, secret
toggle, bulk .env paste (comments, export prefix, quoted values), and
inline validation for empty/invalid/duplicate keys. Parser and validator
live in variable-utils.ts with unit coverage.
Generated OpenAPI types drive the new updateServiceVariables client call.
ROADMAP: Variables UX marked done; per-environment values are already
structural — each environment owns its service instances.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
type=database now provisions a managed database via POST /databases
(engine + plan pickers) instead of a hollow service row; template source
navigates to the existing catalog with the project preselected
(/templates?project= now seeds the deploy target). api-client gains
createManagedDatabase + DatabaseEntity from generated schemas.
Authored By: TDvorak <[email protected]>
openapi.yaml: 29 -> 114 paths / 154 operations covering every mounted
route, including root probes (/live, /health, /ready) and /api/agents/*
token routes via per-path servers overrides. Schemas corrected against
the real Go structs (Service, GitProvider/GitRepository/GitBranch,
NodeAgent capabilities/resources, HostMonitoring, UpgradeStatus,
service metrics, project stats); stale source/build_config contract and
phantom ServiceSource/BuildConfig/ResourceConfig schemas removed.
Proxmox handlers are never registered - intentionally undocumented.
api-client.ts: raw wire types now alias generated schemas
(ServiceVariable, UpgradeStatus, HostMonitoring, NodeAgent, Service,
ServiceMetrics, Git*); CreateServiceInput/CreateUserInput use the
generated request types. CamelCase view models and normalizers remain -
they are the frontend contract, not duplication.
Frontend gate green: tsc, eslint, vitest, vite build.
Authored By: TDvorak <[email protected]>
Backend:
- GET /services/:id/metrics with real Docker container stats
- DELETE /git/providers/:id and GET repo branches endpoint
Frontend:
- Service detail shows real telemetry or honest empty states
- Live status distinguishes idle/polling/reconnecting
- Git providers section in Settings; repo+branch pickers in Add Service
- Real logo, compact Projects header, canvas fitView fix
- index.css deduplicated (1725 -> 1384 lines)
- Fixed smoke tests, lint errors, ToastProvider/context unification
Repo hygiene:
- Added LICENSE (MIT), CONTRIBUTING.md, SECURITY.md; rewrote README
- Untracked .env.prod and data/letsencrypt secrets; fixed .gitignore
- Removed legacy root src/internal/cmd and stale configs/docs/artifacts
- Moved guides and design references under docs/
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>