-
Notifications
You must be signed in to change notification settings - Fork 1
Build Automation
Two ways to fire builds without clicking "build" in the UI:
| Mechanism | Trigger |
|---|---|
| Cron schedule | Background scheduler, ticks every 60 s |
| External webhook |
POST /api/webhooks/build/{projectId} from another system |
Both are managed at /projects/{id}/automation (project detail →
"⏰ Automation").
Looking for console prompt automation (repeat/sequence prompts on every turn completion)? See Prompt Automation.
The cron is intentionally minimal — no day-of-week, month, or day-of-month. Only:
| Pattern | Meaning |
|---|---|
HH:MM |
Fire daily at this UTC-local time (e.g. 02:00) |
*:MM |
Fire hourly at this minute (e.g. *:30) |
*:* |
Fire every minute — test only |
Full vixie-cron is on the roadmap; for now, more complex schedules can be synthesized by adding multiple entries (e.g. 3 rows for "Mon/Wed/Fri 02:00" isn't possible yet, but "02:00 daily" is one row).
BuildScheduler ticks every 60 s. For each enabled schedule whose
expression matches the current hour/minute, it:
- Calls
BuildService.enqueueDebug(projectId, hub). - Writes
lastFiredAt(ISO instant). - Records
audit_logrow:schedule.create/schedule.deletefor lifecycle; the actual build firing shows up as a normalbuild.enqueuerow.
Per-minute dedupe: if lastFiredAt starts with the current
yyyy-MM-dd'T'HH:mm, the tick is skipped. So even if the scheduler ticks
twice in the same minute (unlikely but possible), only one build fires.
Each row has a "■ 비활성" toggle. Disabled rows are kept in the table for later re-enablement without losing the description.
- On
/projects/{id}/automation, click + 새 secret. - The page reloads with a one-time yellow card showing the secret
(32-byte URL-safe random, e.g.
Vk-jWcFx_h2…). Copy it now — this is the only time it's shown. - The DB stores only
SHA-256(secret)hex, not the secret itself.
SECRET='Vk-jWcFx_h2_e1F9...' # value you copied at step 2
SECRET_ID='abc123...' # from the "secret id" column
BODY='{"trigger":"github-actions","sha":"deadbeef"}'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
curl -X POST http://<host>:17880/api/webhooks/build/my-app \
-H "X-Vibe-Secret-Id: $SECRET_ID" \
-H "X-Vibe-Secret: $SECRET" \
-H "X-Vibe-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
--data "$BODY"Response on success:
{ "projectId": "my-app", "triggered": true }-
X-Vibe-Secret-Id— selects which DB row to verify against. -
X-Vibe-Secret— plaintext (TLS expected). Server computesSHA-256(plaintext)and compares to the stored hash with constant-time equality. -
X-Vibe-Signature— optional HMAC-SHA256(secret, body) hex. If present and the body is non-empty, server verifies. Provides body integrity even if TLS is broken downstream.
The design splits the two so:
- A misconfigured reverse-proxy that strips bodies (some legacy gateways) still lets the trigger fire — the Secret header is enough.
- A
body-aware integration (GitHub-style) can add Signature for defense-in-depth without breaking the simpler case.
A pure HMAC-only design (sender knows secret, server only stores hash) is
prevented by storing only SHA-256(secret) — verifying HMAC would require
the plaintext. Storing plaintext is worse, so the X-Vibe-Secret transit
is the compromise. HTTPS reverse proxy mandatory when exposing this
endpoint outside LAN.
Add as many as you need — one per integration is the recommended pattern
(e.g. github-actions, gitlab-ci, monitoring). Each has its own
secret id and last-used timestamp, so revoking a single integration
doesn't break the others.
There is no built-in rate limit on the webhook. The
underlying BuildService.enqueueDebug will queue every call; a runaway
caller can fill the build queue. Mitigation:
- Put the webhook endpoint behind a reverse proxy that does IP-based rate limiting.
- Add a one-shot secret-rotation if you suspect leak.
| Action | Audited as |
|---|---|
| Schedule create |
schedule.create (detail: cron) |
| Schedule delete | schedule.delete |
| Webhook secret create |
webhook.secret.create (detail: name) |
| Webhook secret delete | webhook.secret.delete |
| External trigger fires build |
webhook.build.trigger (detail: secretName) |
Sees the Audit Log page for the schema.
- Full vixie-cron expressions (
0 2 * * 1-5etc.) — drop-in library evaluation under consideration. - Per-secret rate limiting (e.g. max 10 builds/min/secret).
- Per-schedule variant override (currently always debug).
- HMAC-only mode (server stores plaintext encrypted with
auth.pepper).