Commit Graph

3 Commits

Author SHA1 Message Date
MechaCat02
f51924fdbc feat: per-script Rhai sandbox overrides with admin ceiling
Adds optional per-script overrides for the six Rhai sandbox knobs
(max_operations, max_string_size, max_array_size, max_map_size,
max_call_levels, max_expr_depth). The executor merges its defaults
with each script's overrides on every call; the manager validates
overrides against an admin-set ceiling at write time, so the
executor trusts whatever is stored.

Storage chose JSONB on the existing scripts table over six new
columns: lets future knobs land as code-only changes, keeps the
sparse common case (most scripts override nothing) cheap to store
and serialize, and matches how the manager + executor pass the
config across the wire.

  * 0002_sandbox.sql — ALTER TABLE scripts ADD COLUMN sandbox
    JSONB NOT NULL DEFAULT '{}'
  * shared::ScriptSandbox — six Option<u64> fields with
    deny_unknown_fields so typos surface as 422
  * Script.sandbox + ExecRequest.sandbox_overrides — typed end
    to end; cluster mode just serializes the same struct
  * executor-core::Limits::with_overrides — field-by-field
    replacement; tests cover the override actually tightening
    the live engine
  * manager-core::SandboxCeiling — built-in conservative
    defaults (10M ops, 1 MiB strings, 100k array/map, 128
    call/expr depth); env vars override per knob, invalid
    values warn-and-skip rather than blocking boot
  * manager-core admin API — POST/PUT accept `sandbox`; values
    above the ceiling return 422 with the specific field +
    requested + ceiling; absent or `{}` keeps platform defaults
  * picloud all-in-one — wires SandboxCeiling::from_env() into
    AdminState
  * memory_limit_mb stays in the schema, marked v1.3+ advisory
    (no enforcement until OS-level isolation lands with
    cluster-mode executors)

Verified live through Caddy:
  * /version reports schema 2, product 0.3.0
  * Script with max_operations: 500 → 507 on a 10k-iteration loop
  * Same script after PUT raising to 1M → succeeds, returns 10000
  * POST with max_operations: 1_000_000_000 → 422 (exceeds ceiling)

Tests:
  * 13 executor-core unit tests (added 2 for override semantics)
  * 20 integration tests (added 6 for sandbox CRUD + ceiling +
    unknown-field rejection + executor honoring overrides)
  * default cargo test --workspace stays green (integration tests
    remain #[ignore]'d until DATABASE_URL is set)

Bumps:
  * schema 1 → 2
  * product 0.2.0 → 0.3.0
  * SDK unchanged (scripts see nothing new)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 16:26:12 +02:00
MechaCat02
0473d295af feat: versioning scheme — lockstep crates + four independent surfaces
Establish how versions are assigned, bumped, and checked across the
five things that actually change for users: the product itself, the
Rhai SDK, the HTTP API, the database schema, and the inter-service
wire (reserved for cluster mode). Crates ship in lockstep — drift
between picloud-shared and picloud-manager-core is fiction since
they always release together — but surfaces are versioned and
checked at their natural boundaries.

  * docs/versioning.md is the authoritative reference: what gets a
    version, the per-surface compatibility rules, how each surface
    bump cascades to the product version (loose pre-1.0, strict
    post-1.0), and the five enforcement mechanisms (lockstep at
    compile time, /version at runtime, golden SDK contract tests,
    migration replay, CI guardrail).

  * shared::version exposes four constants — PRODUCT_VERSION (from
    CARGO_PKG_VERSION), SDK_VERSION ("1.0"), API_VERSION (1),
    WIRE_VERSION (1). Scripts read SDK_VERSION as ctx.sdk_version
    and can feature-detect against it.

  * Workspace inheritance: `[workspace.package] version = "0.2.0"`
    is the single point of truth; every crate uses
    `version.workspace = true`. dashboard/package.json mirrors.

  * Routes move to /api/v1/* — both control plane
    (/api/v1/admin/*) and data plane (/api/v1/execute/{id}).
    Picloud composes them via a single `/api/v{API_VERSION}` nest,
    so the next major is a copy-paste-and-bump. Caddyfile (dev and
    prod) routes /api/v1/* to picloud and 404s any other /api/*
    so old clients fail loudly instead of getting the SPA shell.
    Dashboard client + integration tests updated.

  * /healthz remains a plain "ok" string (k8s probes); /version is
    the new JSON endpoint returning every surface version in one
    place — product, sdk, api, schema (from
    manager-core::migrations::latest_version), wire.

  * Reasonable bump rationale: API path changes are breaking by
    definition, so 0.1.0 → 0.2.0 (pre-1.0 license to bump minor on
    any breaking change). SDK starts at 1.0 because scripts depend
    on it more strictly than the product depends on its internals;
    we'd rather promise SDK stability early than pull the rug.

Verified live:
  * /healthz → "ok" (plain text)
  * /version → {product:"0.2.0",sdk:"1.0",api:1,schema:1,wire:1}
  * /api/v1/admin/scripts → 200
  * /api/admin/scripts → 404 with error JSON (sunset major)
  * Script can read ctx.sdk_version → "1.0"
  * All 14 integration tests pass against new paths
  * 11 executor-core unit tests pass (added one for sdk_version
    exposure with the major.minor format invariant)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:31:08 +02:00
MechaCat02
dca36a30d2 feat(dashboard): scaffold SvelteKit SPA for control plane
SvelteKit 2 + Svelte 5 (runes) + TS, built with `adapter-static`
into a single SPA bundle that Caddy serves verbatim in production.
The dashboard targets only `/api/admin/*` (manager); data-plane
invocations go through the orchestrator, not through here.

  * Vite dev server proxies `/api` and `/healthz` to PICLOUD_API
    (default `http://127.0.0.1:18080` to match the picloud bind
    override). Port configurable via PICLOUD_DASHBOARD_PORT.
  * `src/lib/api.ts` is a thin typed client over the control-plane
    paths; the scripts placeholder route shows the "load → error →
    list" shape so the missing-API state is informative, not blank.
  * SSR disabled at the layout level: the build is a pure SPA, no
    Node runtime is required at serve time.
  * `npm run check` and `npm run build` both green; `npm audit`
    clean (cookie override pins past the SvelteKit transitive
    advisory that doesn't apply in SPA mode).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:28:03 +02:00