VSKI v0.15.1
The vski CLI and declarative IaC (vski-sdk 0.9.0)
Section titled “The vski CLI and declarative IaC (vski-sdk 0.9.0)”v0.15.0 made pipelines, secrets and schedules first-class server citizens — all of it managed so far through the dashboard and ad-hoc curl. v0.15.1 adds the dev and devops standard on top: a Python CLI that covers the full management API, and a versioned YAML format that converges a deployment from files in git — collections, pipelines, cron jobs, webhooks, secrets, plugin instances and graphs — with plan/apply/pull semantics and CI-friendly exit codes.
pip install "vski-sdk[cli]" \ --extra-index-url https://vski.sh/api/packages/x/pypi/simple/
export VSKI_URL=https://vski.example.comvski keys create dev --roles admin # raw key shown onceexport VSKI_API_KEY=vski_...
vski statusvski plan # diff ./vski against the deploymentvski apply --yes # convergevski pull # export live state back to YAMLThe CLI
Section titled “The CLI”Every command resolves its target the same way — flags >
VSKI_URL/VSKI_API_KEY/VSKI_TOKEN/VSKI_DB env > a .env file >
~/.vski/config.json (written by vski login; same file the old Deno CLI
used, so workstations carry over). Admin-role API keys are full admins; a
workflows-role key scopes to pipelines and secrets. --json makes every
command scriptable.
Command surface: login/logout/whoami/status, db, collections,
records (CRUD + search), sql/query, migrations
(list/status/generate/apply/rollback/upload/export), pipelines
(list/show/validate/run/tokens/trigger), runs (list/show/events/cancel
over workflow and pipeline runs alike), cron, webhooks, graphs,
secrets, plugins + plugins instances (start/stop/restart/invoke/logs),
keys, replicas — and the IaC verbs plan/apply/pull, both as
top-level commands and under vski iac.
IaC v1 — the format
Section titled “IaC v1 — the format”A deployment’s desired state lives in a vski/ directory; every YAML file
under it is merged (recursively, in path order). The format is strict: each
file declares version: 1, and unknown keys at any level are errors, never
warnings — a typo never deploys half a resource.
version: 1database: default # scopes collections/graphs/plugins
collections: notes: fields: - {name: title, type: text, required: true} rules: {list: "*", create: null} # null ≡ "" = admin-only indexes: [{name: idx_created, sql: "CREATE INDEX idx_created ON notes(created)"}] fts: {fields: [title, body]}
pipelines: # global; the map key is the name note-intake: triggers: [{type: collection, collection: notes, actions: [create]}] steps: [{id: enrich, type: plugin, target: hello/echo, params: {message: "{{trigger.record.title}}"}}]
cron: {nightly: {schedule: "0 2 * * *", type: http, url: "pipeline://note-intake"}}secrets: {openai_key: {fromEnv: OPENAI_API_KEY}} # values never in YAMLplugins: {hello: {plugin: hello, config: {greeting: hi}}}graphs: {knowledge: {rules: {list: "*"}}}databases: {analytics: {description: Analytics}} # ensure-exists onlySemantics worth knowing:
- Sub-blocks are opt-in per resource. An omitted
rules:block is left unmanaged; a present one is authoritative.fieldsis required when creating a collection; modifying an existing field (type, required, …) is rejected with an explanation — migrations support add/drop only. - Collection changes always go through the migration system. The CLI
diffs desired vs live, renders an ordinary migration file (the same
format the dashboard’s generator emits), uploads it via
POST /api/migrations/saveand applies it — schema changes keep their audit trail, pre-migration snapshots and rollback. `—save-migrations` also writes the generated YAML to disk for review artifacts. - Secrets are references, never values. Apply reads the env var at
execution time; a new secret whose env var is unset is a warning, not
drift, so
plancan gate CI on convergence in environments that legitimately don’t hold the value. - Deletion is deliberate.
state: absentremoves one resource (collections through a softdrop_tablemigration);--pruneopts into deleting undeclared resources of the kinds your files manage. Databases are never dropped by IaC. - Pipelines are validated server-side before anything is applied — an invalid definition aborts the apply with the per-step issue list.
planexits0clean,1on error,2on drift — a drop-in CI gate.pullexports live state (rules, indexes, views, FTS fields, pipeline definitions, plugin configs) in the exact formatapplyconsumes; the round trip is a no-op by construction and covered by tests. Secret values come out asfromEnv: SECRET_<NAME>placeholders.
Template demo
Section titled “Template demo”The app template is the reference IaC
repository: vski/ declares the notes collection, a hello plugin instance,
a note-intake pipeline and a nightly pipeline:// kick; make iac-plan/iac-apply/iac-pull wrap the CLI, and the e2e suite proves the
loop end-to-end — a new note triggers the pipeline through the plugin’s
warm function. New wiki pages: CLI
and IaC.
Fix: FTS sync triggers broke all record writes on indexed collections
Section titled “Fix: FTS sync triggers broke all record writes on indexed collections”CreateFtsTableInDB generated its sync triggers with bare column
references (VALUES (new.id, title, body) — unresolvable inside the
trigger) and keyed the index on the TEXT id column while FTS5 rowids are
INTEGER. Net effect: inserting or updating a record in any collection
with an FTS index failed (no such column: <field> / datatype mismatch). Triggers now read new.*/old.* qualified columns and key on
the table’s implicit integer rowid (content_rowid='rowid'), matching the
search path (rowid IN (SELECT rowid FROM <fts> ...)).
Existing FTS indexes created by older builds still carry the broken
triggers — recreate them once (drop and re-add the FTS index in the
dashboard, or declare fts: in IaC and let apply drop/create it).
Python SDK repairs (vski-sdk 0.9.0)
Section titled “Python SDK repairs (vski-sdk 0.9.0)”vski.api.secretswas unimportable since v0.15.0 (a broken merge left a wrong base-class import and dead duplicated methods) — the secrets namespace now loads and is exercised by tests.SyncVskiClientwas missing thepipelines,secretsandpluginsnamespaces (the sync mirror had not kept up with the async client).settings.migrations.save(migration)added — posts a MigrationFile JSON body toPOST /api/migrations/save(the upload endpoint round-trips a file; save is what IaC apply uses)._version.pyresynced withpyproject.toml(was two versions behind).
The minimal Deno CLI in @vski/sdk (login/db/migrate) is deprecated in
favor of the Python CLI; its README says so.
- Python SDK: 30 new unit tests (config precedence, strict loader, the diff
engine including the pull→apply round-trip no-op property) and ten new
e2e tests across two suites — the CLI surface (login/keys/db/secrets/sql)
and the IaC lifecycle (plan drift, apply, convergence, field add/drop +
rule/pipeline mutations,
state: absent, pull round-trip against a fresh deployment, field-modification rejection). The full e2e suite — 117 tests — passes. - Server: the repositories suite passes over the FTS trigger change; the template’s e2e covers an FTS-indexed collection receiving writes through a pipeline — the case the old triggers broke.
Compatibility
Section titled “Compatibility”- Additive: no API, storage or wire changes beyond the new SDK version and
the corrected FTS trigger DDL.
vski-sdk 0.9.0keeps the 0.8 API; the CLI arrives as an optional extra (pip install vski-sdk[cli]). - The FTS fix changes generated trigger DDL only; indexes created by older binaries must be recreated once to pick up working triggers (see above).
- IaC manages what its files declare: adopting it against an existing
deployment starts as a no-op —
vski pullfirst, review, commit, then letplangate your CI.