11# AI Agent Instructions for CMS (Rust + React)
22
3+ ## Product Identity
4+
5+ - Human-facing product and service display name: ** Velopulent CMS** .
6+ - Canonical documentation: < https://cms.velopulent.com/docs > .
7+ - Keep the executable, package name, native service identifier, environment prefix, and internal identifiers as ` vcms ` / ` VCMS_* ` for backward compatibility. Do not rename persisted paths or service keys when updating branding.
8+
39## Architecture
410
511- ** Backend** : Rust + Axum HTTP server, ` SQLx ` + (SQLite | PostgreSQL | MySQL), ` rust-embed ` (static assets)
612- ** Frontend** : React in ` apps/dashboard/ ` with Tanstack Router, Tanstack Query, shadcn/ui
713- ** gRPC** : Separate server on port 50051 (compiled from ` libs/proto/*.proto ` via ` tonic-build ` )
814- ** Build** : Nx orchestrates ` dashboard:build ` → ` backend:build ` . ` build.rs ` compiles proto files only.
9- - ** Runtime** : Single binary serves REST API (` /api/* ` ), gRPC, GraphQL (` /api/graphql ` ), MCP (Streamable HTTP at ` /mcp ` ), and static SPA fallback
15+ - ** Runtime** : Single binary serves REST API (` /api/* ` ), gRPC, GraphQL (` /api/graphql ` ), MCP (Streamable HTTP at ` /mcp ` ), health probes ( ` /health/live ` , ` /health/ready ` ), and static SPA fallback
1016
1117## Key Directories
1218
2531- ` libs/proto/ ` - Protocol Buffer definitions (` cms.proto ` )
2632- ` apps/dashboard/ ` - React frontend app
2733- ` apps/web/ ` - Landing Page and Documentation (NextJS + Fumadocs)
34+ - ` packaging/ ` - Native Linux, macOS, Windows, Debian, RPM, and Arch definitions and lifecycle scripts
35+ - ` xtask/ ` - Typed release/package orchestration; platform builders live in separate modules
2836
2937## Developer Commands
3038
@@ -75,16 +83,35 @@ vcms admin reset-password --email U --password P
7583vcms backup create [--scope instance| site] [--site ID] [--out FILE] [--no-files] [--encrypt]
7684vcms backup list # list recorded backups
7785vcms restore --file PATH [--scope instance| site] [--site ID] [--import-as-new] --yes
86+ vcms mcp stdio # thin HTTP proxy to a running server's /mcp (for MCP clients)
87+ vcms service status # normalized native-service status and manager details
88+ vcms doctor # validate config, storage, database, bind ports, and service identity
7889```
7990
8091` backup ` /` restore ` run offline (no HTTP server) against the configured database —
8192the disaster-recovery path when the instance won't boot. ` restore ` is destructive
8293and requires ` --yes ` .
8394
95+ ` mcp stdio ` opens no database, secrets, or search index of its own: it forwards
96+ JSON-RPC between stdin/stdout and the server's ` /mcp ` Streamable-HTTP endpoint,
97+ reading only ` VCMS_MCP_TOKEN ` (bearer) and ` VCMS_MCP_URL ` (default
98+ ` http://127.0.0.1:3000 ` ). So it works even when the data is owned by the OS-service
99+ account. The installed service pins ` VCMS_HOME ` to a system dir so the daemon stores
100+ everything under one owned root.
101+
84102Global flags (highest precedence): ` --config <PATH> ` , ` --bind <ADDR> ` , ` --database-url <URL> ` , ` --log-level <LEVEL> ` .
85103
86104The server auto-migrates the database on every startup; there is no separate migrate command.
87105
106+ ## Packaging and Releases
107+
108+ - Keep native package definitions and service files in ` packaging/ ` ; do not embed them in Rust source or workflow YAML.
109+ - ` xtask ` orchestrates deterministic staging and packaging. Keep ` xtask/src/main.rs ` limited to CLI parsing and dispatch, with shared and platform-specific implementation in modules.
110+ - Keep the release workflow thin: build the dashboard, run native build/package jobs, assemble artifacts, attest, and publish.
111+ - Ordinary CI validates packaging templates, ` xtask ` tests, and deterministic dry-runs. It must not install or mutate host services.
112+ - Release artifacts include portable archives, Debian, RPM, MSI, and macOS PKG packages. Arch is maintained as a package recipe consuming published Linux archives; render it with ` xtask arch-render ` , not as a fake release archive.
113+ - The stable native service identifier is ` vcms ` ; its human-facing display name is ** Velopulent CMS** . Fresh Linux and macOS package installs register/enable the service but do not auto-start it; the Windows MSI installs the service for automatic startup and starts it immediately.
114+
88115## Configuration
89116
90117Non-secret settings live in a TOML config file; secrets stay in the environment (or ` .env ` ).
@@ -93,29 +120,53 @@ Layers merge with precedence: **CLI flag > env var > config file > built-in defa
93120Config file search order (first existing wins; missing is fine):
941211 . ` --config ` flag / ` VCMS_CONFIG ` env
951222 . ` ./vcms.toml ` (current dir)
96- 3 . ` ~/.vcms/ config.toml` (CMS home ; ` $VCMS_HOME/config.toml ` if set ) — where ` vcms config init ` writes
123+ 3 . the platform config dir ( ` config.toml ` ; ` $VCMS_HOME/config.toml ` in single-dir mode ) — where ` vcms config init ` writes
971244 . ` /etc/vcms/config.toml `
98125
99- ## Data directory (CMS home)
126+ ## Data directory
127+
128+ Resolution lives in ` apps/backend/src/paths.rs ` and has two layouts:
100129
101- All runtime files live under one home directory: ` $VCMS_HOME ` if set, else ` ~/.vcms `
102- (same layout on Windows, macOS, Linux via the ` directories ` crate). ` vcms serve `
103- creates it on first run. Resolution lives in ` apps/backend/src/paths.rs ` .
130+ ** Split (default, interactive installs)** — files land in the platform-conventional
131+ per-type directories via the ` directories ` crate (` ProjectDirs ` ):
132+
133+ | File(s) | Dir | Linux | macOS | Windows |
134+ | ---------| -----| -------| -------| ---------|
135+ | ` config.toml ` , ` secrets.toml ` , ` .env ` | config | ` ~/.config/vcms ` | ` ~/Library/Application Support/vcms ` | ` %APPDATA%\vcms\config ` |
136+ | ` vcms.db ` , ` storage/ ` , ` backups/ ` | data | ` ~/.local/share/vcms ` | ` ~/Library/Application Support/vcms ` | ` %APPDATA%\vcms\data ` |
137+ | ` search/ ` (derived, rebuildable) | cache | ` ~/.cache/vcms ` | ` ~/Library/Caches/vcms ` | ` %LOCALAPPDATA%\vcms\cache ` |
138+ | ` logs/ ` | state | ` ~/.local/state/vcms ` | ` ~/Library/Application Support/vcms ` | ` %LOCALAPPDATA%\vcms\data ` |
139+
140+ (` logs/ ` uses the state dir where the platform has one — Linux — else the local data dir.)
141+
142+ ** Single** — everything nests under one root. Chosen (in precedence order) when:
143+ 1 . ** ` $VCMS_HOME ` is set** — forces the root explicitly.
144+ 2 . ** the system service home dir exists** — Linux ` /var/lib/vcms ` , macOS
145+ ` /Library/Application Support/vcms ` , Windows ` C:\ProgramData\vcms ` . The
146+ platform installer creates it (and leaves it behind on uninstall), so a plain
147+ ` vcms serve ` /` admin ` /` backup ` ** follows the service's data instead of forking to a
148+ per-user split store** . This path is defined once in ` paths::system_home() ` and
149+ imported by the Windows SCM host.
150+ 3 . ** a legacy ` ~/.vcms ` exists** — an existing install keeps working untouched.
151+
152+ Otherwise (dev/eval boxes with no service) files use the platform split dirs.
104153
105154``` text
106- ~/.vcms/
107- config.toml # non-secret config (vcms config init target)
108- secrets.toml # auto-generated HMAC_SECRET + backup key (0600 on unix)
109- vcms.db # default SQLite database (+ -wal / -shm)
110- logs/ # rolling logs when [log] output = "file"
111- storage/ # default filesystem storage for uploads
155+ $VCMS_HOME/ # or system home, or ~/.vcms (legacy)
156+ config.toml secrets.toml .env
157+ vcms.db (+ -wal / -shm) logs/ storage/ backups/ search/
112158```
113159
114- Secrets: on first ` serve ` /` admin ` , a random ` HMAC_SECRET ` is generated
115- and persisted to ` secrets.toml ` (` apps/backend/src/secrets.rs ` ), then loaded by
116- every process — including ` vcms mcp stdio ` , which is launched from an arbitrary cwd
117- and so cannot rely on a cwd ` .env ` . Env vars still override the file. ` mcp stdio `
118- is read-only: it never creates the home dir, database, or secrets file.
160+ When the active home is the system service home (owned by SYSTEM/root), the
161+ data-touching commands (` serve ` /` admin ` /` backup ` /` restore ` ) require elevation — a
162+ non-elevated invocation ** fails fast** with an "Administrator/root" hint (in
163+ ` paths::ensure ` 's preflight) rather than silently forking to a second store.
164+
165+ Secrets: on first ` serve ` /` admin ` , a random ` HMAC_SECRET ` is generated and persisted to
166+ ` secrets.toml ` (` apps/backend/src/secrets.rs ` ), then loaded by the server processes.
167+ ` vcms mcp stdio ` does ** not** load secrets, the database, or any data-dir file: it is a
168+ thin HTTP proxy to the running server's ` /mcp ` (see CLI), forwarding ` VCMS_MCP_TOKEN ` as
169+ the bearer; the server owns all disk I/O.
119170
120171Env-only secrets (never read from ` config.toml ` by convention, omitted from
121172` config init ` ): ` DATABASE_URL ` , ` HMAC_SECRET ` , ` S3_ACCESS_KEY_ID ` ,
@@ -153,8 +204,10 @@ Logging keys map to the `[log]` table: `RUST_LOG`→`log.level`, `LOG_OUTPUT`→
153204| Variable | Default | Description |
154205| ----------| ---------| -------------|
155206| ` VCMS_CONFIG ` | - | Explicit config file path (same as ` --config ` ) |
156- | ` VCMS_HOME ` | ` ~/.vcms ` | CMS home directory (db, config, secrets, logs, storage) |
157- | ` DATABASE_URL ` | ` sqlite://~/.vcms/vcms.db ` | Database URL: ` sqlite:path ` , ` postgres://... ` , ` mysql://... ` |
207+ | ` VCMS_HOME ` | - | If set, forces single-dir mode: db/config/secrets/logs/storage all nest under this root (else files use the platform split dirs) |
208+ | ` VCMS_MCP_TOKEN ` | - | ` vcms_site_* ` access token forwarded by ` vcms mcp stdio ` as the bearer credential (required for stdio) |
209+ | ` VCMS_MCP_URL ` | ` http://127.0.0.1:3000 ` | Running server's base URL that ` vcms mcp stdio ` proxies to (` {url}/mcp ` ) |
210+ | ` DATABASE_URL ` | ` sqlite://<data dir>/vcms.db ` | Database URL: ` sqlite:path ` , ` postgres://... ` , ` mysql://... ` |
158211| ` HMAC_SECRET ` | auto | HMAC key for token lookup (required; auto-generated to ` secrets.toml ` , env overrides) |
159212| ` BIND_ADDRESS ` | ` 0.0.0.0:3000 ` | REST API listen address |
160213| ` GRPC_BIND_ADDRESS ` | ` 0.0.0.0:50051 ` | gRPC server listen address |
@@ -167,14 +220,15 @@ Logging keys map to the `[log]` table: `RUST_LOG`→`log.level`, `LOG_OUTPUT`→
167220| ` S3_PUBLIC_URL ` | - | Public URL for S3 assets |
168221| ` BACKUP_ENABLED ` | ` true ` | Run the scheduled-backup poller / allow backups |
169222| ` BACKUP_DESTINATION ` | ` filesystem ` | Backup destination: ` filesystem ` or ` s3 ` |
170- | ` BACKUP_LOCAL_PATH ` | ` ~/.vcms /backups` | Local backup dir (when destination is filesystem) |
223+ | ` BACKUP_LOCAL_PATH ` | ` <data dir> /backups` | Local backup dir (when destination is filesystem) |
171224| ` BACKUP_ZSTD_LEVEL ` | ` 12 ` | zstd compression level for backups |
172225| ` BACKUP_DEFAULT_RETENTION ` | ` 7 ` | Default "keep last N" for new schedules |
173226| ` BACKUP_S3_BUCKET ` / ` _REGION ` / ` _ENDPOINT ` / ` _PUBLIC_URL ` | - | S3 backup destination (non-secret parts) |
174227| ` BACKUP_S3_ACCESS_KEY_ID ` | - | S3 backup access key (secret, env-only) |
175228| ` BACKUP_S3_SECRET_ACCESS_KEY ` | - | S3 backup secret key (secret, env-only) |
176229| ` BACKUP_ENCRYPTION_KEY ` | auto | AES-256 backup key (hex); auto-generated to ` secrets.toml ` |
177230| ` MAX_UPLOAD_SIZE_MB ` | ` 50 ` | Max upload size in MB |
231+ | ` UPLOAD_TOKEN_EXPIRY_SECS ` | ` 900 ` | Signed upload URL lifetime (seconds) |
178232| ` COOKIE_SECURE ` | ` false ` | Require HTTPS cookies |
179233| ` DB_MAX_CONNECTIONS ` | ` 10 ` | Max DB connections |
180234| ` DB_MIN_CONNECTIONS ` | ` 2 ` | Min DB connections |
@@ -185,10 +239,10 @@ Logging keys map to the `[log]` table: `RUST_LOG`→`log.level`, `LOG_OUTPUT`→
185239| ` LOG_OUTPUT ` | ` stdout ` | ` stdout ` or ` file ` (` [log] output ` ) |
186240| ` LOG_FORMAT ` | ` pretty ` | ` pretty ` or ` json ` (` [log] format ` ) |
187241| ` LOG_ANNOTATIONS ` | ` false ` | Include file + line numbers (` [log] annotations ` ) |
188- | ` LOG_DIR ` | ` ~/.vcms /logs` | Log directory when ` output = file ` (` [log] dir ` ) |
242+ | ` LOG_DIR ` | ` <state dir> /logs` | Log directory when ` output = file ` (` [log] dir ` ) |
189243
190- ** Note** : ` HMAC_SECRET ` is auto-generated and persisted to
191- ` ~/.vcms/ secrets.toml` on first run. Set it explicitly via env to override.
244+ ** Note** : ` HMAC_SECRET ` is auto-generated and persisted to the config dir's
245+ ` secrets.toml ` on first run. Set it explicitly via env to override.
192246
193247## Proto Compilation
194248
0 commit comments