@cloud-cli/on 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,9 @@ Designed with a strict **security-first boundary**, native **JavaScript AST eval
15
15
  - **Deterministic Field Evaluation:** No silent fallbacks or ambiguous type conversions. Plain strings remain literal strings; conditions in `if:` fields run as strict JS boolean expressions.
16
16
  - **Built-in Dark Mode Web UI (`/runs`):** Monitor job statuses live, inspect workspace inputs, and view ANSI-colored terminal log streams rendered in real-time.
17
17
  - **System & Container Execution Drivers:** Run steps directly as detached host process groups or inside isolated Docker/Systemd transient units.
18
- - **Automatic Secret Redaction:** Secrets loaded from `.env` are automatically masked (`***`) across all terminal log outputs and report snapshots.
18
+ - **DB-authored workflows:** Draft, validate, and publish portable YAML workflows through the authenticated API.
19
+ - **Scheduled execution:** Published workflows can run from cron expressions or local sunrise/sunset events.
20
+ - **Encrypted secret storage:** Secret ciphertext is stored in the database; the master key remains on the HTTP server.
19
21
 
20
22
  ---
21
23
 
@@ -23,10 +25,6 @@ Designed with a strict **security-first boundary**, native **JavaScript AST eval
23
25
 
24
26
  ```text
25
27
  my-project/
26
- ├── on/ # Workflow definitions directory
27
- │ ├── release.yml
28
- │ └── test.yml
29
- ├── .env # Local secrets (git-ignored)
30
28
  ├── runner.config.mjs # (Optional) Engine configuration
31
29
  └── package.json
32
30
 
@@ -41,24 +39,14 @@ my-project/
41
39
  Run the engine directly via `npx` or `pnpm dlx`:
42
40
 
43
41
  ```bash
44
- # Start full engine (Ingress HTTP Gateway + 5 Worker Loops)
45
- npx @cloud-cli/on start
42
+ # Start each role (normally managed by the supplied systemd units)
43
+ npx @cloud-cli/on start-server
44
+ npx @cloud-cli/on start-scheduler
45
+ npx @cloud-cli/on start-workers
46
46
 
47
47
  ```
48
48
 
49
- ### 2. Configure Secrets (`.env`)
50
-
51
- Secrets are automatically loaded from `.env` at the root of your project. Prefix secrets with `SECRET_`:
52
-
53
- ```env
54
- SECRET_NPM_TOKEN="npm_1234567890abcdef"
55
- SECRET_GITHUB_TOKEN="ghp_1234567890abcdef"
56
- SECRET_GITHUB_WEBHOOK_SECRET="my-webhook-secret"
57
- ```
58
-
59
- `SECRET_GITHUB_WEBHOOK_SECRET` is required to validate incoming webhooks from GitHub
60
-
61
- ### 3. Define a Workflow (`on/release.yml`)
49
+ ### 2. Define a Workflow
62
50
 
63
51
  ```yaml
64
52
  name: Build and Publish Release
@@ -110,6 +98,32 @@ steps:
110
98
  npx --yes semantic-release@24 -b main --no-ci
111
99
  ```
112
100
 
101
+ Store the YAML as a draft and publish it through the authenticated API. Only published revisions can receive webhooks or scheduled runs:
102
+
103
+ ```bash
104
+ curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/validate \
105
+ -H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
106
+ curl -u admin:"$RUNNER_ADMIN_SECRET" -X PUT http://localhost:11235/api/workflows/build-and-publish-release \
107
+ -H 'Content-Type: application/json' --data '{"sourceYaml":"..."}'
108
+ curl -u admin:"$RUNNER_ADMIN_SECRET" -X POST http://localhost:11235/api/workflows/build-and-publish-release/publish
109
+ ```
110
+
111
+ Time triggers are defined beside a webhook trigger:
112
+
113
+ ```yaml
114
+ on:
115
+ schedule:
116
+ - id: nightly
117
+ cron: "0 2 * * *"
118
+ timezone: Europe/Berlin
119
+ solar:
120
+ - id: morning
121
+ event: sunrise
122
+ latitude: 52.52
123
+ longitude: 13.405
124
+ offset: +15m
125
+ ```
126
+
113
127
  GitHub triggers support these preprocessor filters:
114
128
 
115
129
  | Field | Match behavior |
@@ -150,8 +164,8 @@ npx @cloud-cli/on [command] [options]
150
164
  | Command | Description |
151
165
  | ------------------- | ---------------------------------------------------------------------------------------- |
152
166
  | **`start-server`** | Runs Webhook Ingress Gateway (the HTTP server receiving webhooks). |
167
+ | **`start-scheduler`** | Dispatches published cron and solar workflow triggers. |
153
168
  | **`start-workers`** | Runs the event-driven worker scheduler (Scalable Workers). |
154
- | **`validate`** | Parses and validates all YAML workflows in your workflows folder without executing jobs. |
155
169
 
156
170
  ### CLI and Environment Options
157
171
 
@@ -160,17 +174,18 @@ npx @cloud-cli/on [command] [options]
160
174
  | `-h` | `--help` | — | - | Prints CLI help message and exits. |
161
175
  | `-c` | `--config` | `./runner.config.mjs` | `RUNNER_CONFIG_FILE` | Path to JavaScript configuration file. |
162
176
  | `-d` | `--database` | - | `RUNNER_DATABASE_URL` | SQLite database file path or HTTP URL. |
163
- | `-w` | `--workflows` | `on/` | `RUNNER_WORKFLOWS` | Directory where workflow YAML files live. |
164
177
  | `-p` | `--port` | `11235` | `PORT` | Port for the Ingress HTTP server. |
165
178
  | `-k` | `--workers` | `5` | `RUNNER_WORKERS` | Maximum concurrent jobs on this node. |
166
179
  | | | | `RUNNER_ADMIN_SECRET` | Admin token to refresh secrets via API |
167
180
  | | | | `RUNNER_SERVER_URL` | Webhook server URL used by workers. |
168
181
  | | | | `RUNNER_TAGS` | Comma-separated worker capability tags. |
169
182
 
170
- Set the same non-empty `RUNNER_ADMIN_SECRET` on the server and workers to publish live job-status refresh events. Job availability and the 60-second recovery refresh continue to work without it.
183
+ Set the same non-empty `RUNNER_ADMIN_SECRET` on the server and workers to publish live job-status refresh events and let workers retrieve job-scoped secrets. The dashboard workflow APIs accept either Bearer authentication or HTTP Basic authentication with username `admin` and this secret.
171
184
 
172
185
  ### Secrets
173
186
 
187
+ Secrets are written through `PUT /api/secrets/:NAME` and are AES-256-GCM encrypted in the database. Set `RUNNER_MASTER_KEY` only on the HTTP server, or provide an `on-master-key` systemd credential. Workers receive decrypted values only after claiming a running job; values are not written to job payloads or reports.
188
+
174
189
  ---
175
190
 
176
191
  ## ⚙️ Configuration Reference
@@ -179,14 +194,13 @@ You can customize engine behavior using `runner.config.mjs` in your project root
179
194
 
180
195
  ```javascript
181
196
  // runner.config.mjs
182
- import { HtmlReporter, SlackReporter, JsonFileReporter } from '@cloud-cli/on/reporters';
197
+ import { GitHubStatusPlugin } from '@cloud-cli/on';
183
198
 
184
199
  export default {
185
200
  port: 3000,
186
201
  workers: 5,
187
202
  serverUrl: 'https://runner.example.com/',
188
203
  tags: ['linux', 'docker'],
189
- workflows: '/home/workflows/',
190
204
  storagePath: '/tmp/workspaces',
191
205
  database: 'https://remote.db.com/',
192
206
 
@@ -195,18 +209,19 @@ export default {
195
209
  NODE_ENV: 'production',
196
210
  },
197
211
 
198
- // Custom execution reporters
199
- reporters: [
200
- new JsonFileReporter({ outputDir: './reports/json' }),
201
- new HtmlReporter({ outputDir: './reports/html' }),
202
- new SlackReporter({
203
- webhookUrl: process.env.SLACK_WEBHOOK_URL,
204
- channel: '#ci-deployments',
212
+ plugins: [
213
+ new GitHubStatusPlugin({
214
+ token: process.env.SECRET_GITHUB_TOKEN,
215
+ context: 'on',
205
216
  }),
206
217
  ],
207
218
  };
208
219
  ```
209
220
 
221
+ The GitHub status plugin publishes commit states when a workflow starts and finishes. Its token needs permission to write commit statuses for the target repository.
222
+
223
+ Run details are available as HTML at `/runs/:id` and as sanitized JSON at `/api/runs/:id`. The HTML view refreshes reactively through job-specific SSE events while a run is active. Both representations omit raw webhook bodies, sensitive input fields, execution environment values, and internal rerun state.
224
+
210
225
  ---
211
226
 
212
227
  ## 📐 Deterministic Evaluation Rules
@@ -243,7 +258,7 @@ Within `${...}`, `if:`, and `eval:` contexts, the following object scopes are ex
243
258
 
244
259
  - **`inputs`**: Payload key-values received from incoming webhooks.
245
260
  - **`env`**: Merged environment variables from global config and workflow definitions.
246
- - **`secrets`**: Unmasked secret values loaded from `.env` or environment variables (`SECRET_` prefix stripped).
261
+ - **`secrets`**: Unmasked job-scoped values from encrypted central secret storage.
247
262
  - **`steps`**: Execution statuses and outputs from previous steps in the workflow (`steps.<id>.status`, `steps.<id>.outputs`).
248
263
  - **`BUILTIN_HELPERS`**: JS utilities including `String`, `Number`, `Boolean`, and `JSON.parse` / `JSON.stringify`.
249
264
 
@@ -278,8 +293,24 @@ The Ingress Gateway listens for incoming HTTP requests and serves the live web U
278
293
  AST evaluation explicitly blocks access to dangerous JS properties (`constructor`, `__proto__`, `prototype`).
279
294
  3. **Payload Size Guard:**
280
295
  The Ingress server enforces a strict 5MB payload limit to prevent Out-Of-Memory (OOM) denial-of-service attacks.
281
- 4. **Signal Traps & Resource Cleanup:**
282
- Graceful process traps (`SIGINT`, `SIGTERM`) ensure active job handles are safely terminated, file descriptors are closed, and temp `.env`/`.out` files are removed via `try ... finally` blocks.
296
+ 4. **Single-operator API authentication:**
297
+ Workflow validation, publishing, secret management, and job details require `RUNNER_ADMIN_SECRET` over HTTPS.
298
+
299
+ ## systemd Deployment
300
+
301
+ Install the unit files from `systemd/`, create a non-login `on` user, and create a root-owned `/etc/on/runner.env` with `RUNNER_DATABASE_URL`, `RUNNER_SERVER_URL`, `RUNNER_ADMIN_SECRET`, `RUNNER_TAGS`, and `RUNNER_TMP`. Use `systemctl edit` for per-machine overrides.
302
+
303
+ Place the server master key in `/etc/on/credentials/on-master-key` with permissions readable only by the `on` user. `runner-server.service` exposes it privately through systemd's credentials directory. Start the primary control plane with `systemctl enable --now runner.target`; enable `runner-worker.service` separately on worker machines. Use `systemctl edit runner-worker.service` for machine-specific labels and paths.
304
+
305
+ ## Database Migration
306
+
307
+ Before deploying the revision-reference queue code to an existing database, run `migrations/20260904_job_workflow_revisions.sql` once through the SQLite HTTPS query service. It adds `jobs.workflow_revision` and `jobs.required_tags`, backfills routing tags, links jobs to the currently published workflow revision, and cancels only pending/running legacy jobs that cannot be linked. New installations receive both columns from `QueueManager.createTables()`.
308
+
309
+ Workers now resolve the immutable revision when they claim a job. A workflow can safely use a checkout step followed by `if: files.exists('Dockerfile')`; `files` is constrained to the job's `workingDir`, so it cannot inspect files outside that workspace.
310
+
311
+ ## Future Security Work
312
+
313
+ This MVP intentionally supports one trusted operator. Before sharing the UI or exposing it beyond a private deployment, add individual accounts and roles, secure browser sessions and CSRF protection, audit logs, worker enrollment credentials, secret key rotation, encrypted systemd credentials, rate limiting, and database high availability.
283
314
 
284
315
  ## Development
285
316
 
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAsB,UAAU,CAAC,MAAM,KAAA,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAyBrE;AAED,wBAAgB,aAAa,CAAC,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,GAAG,YAAY,CAgB7G;AAED,wBAAgB,SAAS,SAwBxB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB9F"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAsB,UAAU,CAAC,MAAM,KAAA,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAmBrE;AAED,wBAAgB,aAAa,CAAC,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,GAAG,YAAY,CAe7G;AAED,wBAAgB,SAAS,SAsBxB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAuB9F"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export { HtmlReporter } from './reporters/html.reporter.js';
3
- export { JsonFileReporter } from './reporters/json-file.reporter.js';
4
- export { SlackReporter } from './reporters/slack.reporter.js';
2
+ export { GitHubStatusPlugin } from './plugins/github-status.plugin.js';
5
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAYA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC"}