Addresses the gaps identified in the last audit.
Restore (was a stub returning "not yet implemented"). Every repository shares
one connection pool, so the database cannot be swapped underneath a live
server. Restore is therefore two-phase: RestoreBackup validates the file and
stages it beside the database; db.New applies it before the pool is opened,
which is the only safe moment. The database being replaced is preserved as
<db>.replaced-<timestamp>, and stale -wal/-shm are removed so SQLite cannot
replay the old journal over the restored file. Validation is strict — SQLite
integrity_check plus a schema probe — because applying an unrelated file
would destroy the install. GET/DELETE /api/v1/backups/restore inspect and
cancel a staged restore. The CLI does both phases at once, since it runs
standalone; `orchestrad backup` was also a stub and now works.
Secret key. With nothing configured the key is generated once and persisted
to <data>/secret.key, so restarts reuse it and moving the stack to another
server is a matter of copying the data directory. Upgrades are handled: if a
database already exists the install was silently running on the legacy
built-in default, so that value is adopted and written out rather than
replaced — generating a fresh key there would make every stored credential
undecryptable. The file is owner-only (ACL-restricted on Windows).
Multi-arch image: buildx now emits linux/amd64 + linux/arm64, matching the
architectures the release binaries already covered. The Dockerfile
cross-compiles via TARGETARCH rather than emulating, so arm64 costs little.
CSRF: the middleware previously checked only that a header was *present* and
was never wired up, and /auth/csrf returned "csrf-token-placeholder". Tokens
are now nonce + HMAC-SHA256 signed with the application secret, validated
properly, and the middleware is mounted on /api/v1. Bearer and API-key
requests are not CSRF-reachable and pass through untouched, so this is
transparent to the SPA and to API clients.
Also: the Windows store import drops CRYPT_EXPORTABLE (the store copy is not
the source of truth — <data>/tls holds the key, so portability is unaffected
and a non-exportable server key is the better posture), the PFX password is
written to server.pfx.password beside the bundle so an operator importing it
by hand does not have to hunt for a password they never chose, and the
"renewed" log line now reflects whether a leaf was actually issued instead of
guessing from its age.
Verified live: backup -> stage -> restart applies and preserves the previous
database; secret key generated, adopted, and read back across restarts with
the credential check confirming decryptability; CSRF endpoint issues real
signed tokens.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The SQLite database now lives at data/db/orchestrad.db instead of the data
root. db.New creates the db/ directory (SQLite will not) and, on first run,
moves a legacy data/orchestrad.db plus its -wal/-shm sidecars into db/ so
existing installations keep their data. Verified live: an existing demo
database migrated into data/db and all data (connections, rules, schedules)
was retained. Test covers the move + idempotency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lower ORCHESTRAD_LOG_MAX_SIZE_MB default from 100 to 5 so log files roll
sooner by default; README updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Database maintenance:
- New MaintenanceService prunes rule_runs (+ their action detail) and
audit_events older than their retention windows, then VACUUMs to reclaim
space. Runs once at startup and then on an interval, bound to the run
context. Configurable via ORCHESTRAD_RUN_RETENTION_DAYS (90),
ORCHESTRAD_AUDIT_RETENTION_DAYS (180),
ORCHESTRAD_MAINTENANCE_INTERVAL_HOURS (24), ORCHESTRAD_MAINTENANCE_VACUUM.
This stops the database growing forever. (Log rotation already existed via
lumberjack: ORCHESTRAD_LOG_MAX_SIZE_MB/_MAX_BACKUPS/_MAX_AGE_DAYS.)
Config import:
- Import now accepts either the wrapped {payload,dryRun} shape or a bare
exported config object, so a file downloaded from Export re-imports
directly (dryRun via ?dryRun=true) — useful for automation.
Test covers retention pruning with FK-cascaded action rows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Proxy / base-URL:
- ORCHESTRAD_TRUSTED_PROXIES now defaults to "local", trusting reverse
proxies in loopback + RFC1918 + link-local/ULA ranges out of the box, so
X-Forwarded-* (client IP, scheme, host) is honored behind an edge proxy
without extra config. New keywords: local/private, all/any, none.
- OIDC redirect URI derivation now uses the trust-gated request base URL
instead of reading X-Forwarded-Proto directly, and audit client IP now
trusts the middleware-rewritten RemoteAddr rather than the raw (spoofable)
X-Forwarded-For header. Both honor forwarded values only from trusted
peers.
Schedules:
- Seed eight built-in schedules on startup (every 5/15/30 min, hourly,
every 6/12h, daily, weekly), idempotent by name, so operators have
ready-made cadences in the Schedules page and the rule editor's schedule
dropdown without hand-building one.
Test covers the trusted-proxy keyword expansion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On Windows, resolve the listen address/port with precedence env > registry
(HKLM\Software\Grace Solutions\OrchestrAD ListenAddress/ListenPort, written by
the MSI wizard) > default. No-op off Windows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Change the default HTTP port from 8080 to 18090 and propagate it through the
Dockerfile (EXPOSE + healthcheck), docker-compose, and the README. Host default
stays 0.0.0.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover the environment-driven config loader (defaults, overrides, secret
from file) and the unauthenticated /health and /api/v1/version handlers
the Docker HEALTHCHECK and bootstrap flow depend on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds four related pieces of first-run configuration surface so the
single binary can be dropped behind a reverse proxy or into a container
with sensible defaults:
- getEnvOrFile: any ORCHESTRAD_* value may be supplied either directly
via environment variable or indirectly via ORCHESTRAD_*_FILE pointing
at a file on disk, matching the Docker / Kubernetes secret idiom.
- ORCHESTRAD_BOOTSTRAP_USERNAME and ORCHESTRAD_BOOTSTRAP_PASSWORD drive
the first-run admin seed (consumed by auth.Bootstrap in a later commit).
- ORCHESTRAD_TRUSTED_PROXIES accepts a comma-separated list of CIDRs
whose X-Forwarded-* headers will be honored by the proxy middleware.
- ORCHESTRAD_ALLOWED_ORIGINS replaces the hardcoded localhost:3000 CORS
allowlist; empty means no cross-origin access, which is the correct
default for the same-origin embedded-UI deployment.
Phase 1 foundations:
- Go backend with Chi router framework
- SQLite database with WAL mode and foreign keys
- Database migrations for users, roles, credentials, AD connections, schedules, rules, and audit
- CLI commands: init, run, install, uninstall, start, stop, migrate, backup, restore, doctor
- Configuration loading from environment variables
- Centralized logging with file rotation (lumberjack)
- Crypto package for Argon2id password hashing and AES-GCM encryption
- Auth service with session management
- Audit service for event logging
- Scheduler with 6-field cron support
- REST API routes scaffolded for all major resources
- CORS support with localhost defaults for development
- Docker support with Dockerfile and docker-compose.yml
- Multi-platform build script (PowerShell)
- Project structure per design specification
Version format: yyyy.MM.dd.HHmm
All PKs are UUIDv4, all timestamps UTC