Local-First Trust Model
Local-First Trust Model
Ciaren runs on your machine and executes your flows against your data. That shapes the security model: the primary trust boundary is "code and data you chose to run," not a multi-tenant server.
What Ciaren assumes
- A single, trusted local user operates the app.
- The user owns the data, the database, and the execution environment.
- No Ciaren-hosted service is required to run flows.
Under those assumptions some power-user features (custom Python nodes, custom SQL) are intentionally available. They run with the privileges of the local process.
What is not a hard boundary
- Plugins and
pythonTransformare not sandboxed. Python code a plugin or a custom node runs has the same access as the Ciaren process. The permission model is a trust and consent boundary (what loads, after you approve it), not an OS-level sandbox. - Local license checks are not unbreakable DRM. They deter casual misuse of premium plugins; a determined user can bypass them.
- Signatures prove integrity and origin, not safety. A validly signed plugin can still be poorly written; signing only proves it wasn't tampered with and came from a key you trust.
Where Ciaren does enforce boundaries
- Secrets stay out of artifacts. Connection passwords are referenced by
environment-variable name (
password_env), never stored in the flow graph,.flowdocuments, or generated code — exported scripts read the secret fromos.environat run time. - Model URIs can't escape the artifact root. A local
model_urimust resolve inside the configured artifact directory (no..traversal, no absolute paths elsewhere); otherwise an MLflowruns://models:/URI is required. - Pickle is refused outright;
.joblibis allowed but is not format-safe — it's confined, not sandboxed. Bare.pkl/.picklemodel files are rejected because loading one executes arbitrary code..joblibis allowed (sklearn pipelines need it) but serializes with pickle under the hood, so a crafted.joblibcan execute code exactly like a raw pickle — the only thing making that path safe is the artifact-root confinement above, which guarantees a.joblibcan only load from a location the server itself controls, never an arbitrary user-supplied path..json(XGBoost's native format) is the one format that's genuinely code-free. Seeapp/ml/security.py. - Plugin code is gated before import. A drop-in plugin that declares permissions is not imported until you approve it, and the app upload flow adds a risk confirmation (an off-by-default acknowledgement toggle) before installing.
- Tampered or incompatible packages are refused. A
.ciarenpluginwhose contents don't match its signature digest never installs, and one whose declaredciaren/api_versionis incompatible with this build is rejected before it can replace a working install. - Hand-edited manifests are caught after install, too. Ciaren pins a SHA-256
digest of a packaged plugin's
ciaren-plugin.jsonat install time; if the file on disk no longer matches (e.g. someone stripslicense_requiredor widenspermissionsby hand) the loader refuses to import that plugin's code. This is best-effort defense in depth, not tamper-proof — the pinned baseline lives in the same user-writable state file, so it deters casual edits, not a determined local attacker. - Optional plugin permission enforcement.
CIAREN_PLUGIN_PERMISSION_ENFORCEMENT=warn|enforcelogs or blocks ungranted network/file-write/subprocess/shell actions by plugin code at runtime (a bar-raiser, not a sandbox — see the caveat above). - Cross-site requests can't drive the local API. Any website you visit can
send requests to
http://127.0.0.1— CORS only stops it from reading the responses. Ciaren therefore refuses state-changing/apirequests whose browserOriginis not aCORS_ORIGINSentry or a local hostname (localhost/127.0.0.1/::1; extend withCIAREN_TRUSTED_HOSTS). This closes the CSRF/DNS-rebinding path where a malicious page installs and enables a plugin behind your back. Non-browser clients (CLI, curl) send noOriginand are unaffected; whenCIAREN_API_TOKENis set the token gate covers this instead.
Hardening for shared / team use (future)
The current model is tuned for local use. Before multi-user, team, or enterprise deployments, add: read-only SQL connection modes, query allow/deny lists and audit logs, per-plugin OS sandboxing, and admin-enforced permission policies. These are deliberately out of scope for the local-first core.