§ Platform / Data
Your data: in, out, accounted for.
Local files stream straight from disk and never leave your device by default. Everything else — the web, buckets, cloud drives, SSH — arrives through connectors: you are asked before anything is fetched, and the keys that open those doors are encrypted on your device and never synced anywhere. And because every byte lands in one database, every dataset, model, and result carries the story of where it came from.
External sources pass one lock
A connector is content, not a feature of the engine. Each one binds three things: what it touches, typed inputs and outputs with an explicit plan for streaming, and the key it needs. None of that is reserved to us — the same shape, the same checks, and the same catalog serve the built-ins and connectors people write themselves alike, so a missing source is something you add, not something you wait for.
Direction is built into the type, not left as a nuance. Bringing data in pulls bytes onto your device — you consent when it is fetched, and after that it is local-first at rest. Sending data out is a separate, stronger consent that names where it is going, and it can never ride in on the permission you gave for bringing something in. And where a browser cannot speak a protocol at all — a tab cannot open a raw network socket — SSH and FTP go through a platform relay, and are declared as exactly that.
A starting catalog, never a ceiling
| Family | Connectors | What opens the door | How it travels, and one honest note |
|---|---|---|---|
| storage | Local files · Uploads · S3 · GCS · Drive · Dropbox | a file picker · request signing · OAuth | Straight from your browser; bucket requests are signed on your device. |
| transfer | The web, in and out · FTP · FTPS · SFTP | API key · password · SSH key | The web goes straight from the browser; SSH and FTP go through the relay — a tab cannot open a raw socket. |
| messaging | Email · Telegram · Outgoing webhooks | password · API key · shared secret | The consent names the recipient; a rehearsal and a record of what was sent are duties, not options. |
| inbound | Incoming webhooks | a rotatable inbox key | A hosted inbox, size-capped and expiring — never something listening on your machine, and not local-first while it is in transit. |
| export | Google Sheets · Colab export | OAuth — only the files you pick | A format converter plus a connector; Colab means export plus a link to open — never running anything remotely. |
Eleven connectors are the starting catalog of early access, not a boundary: the kinds and the families are open lists anyone can add to, and every row passed the same check a connector you write does.
The starting catalog covers eleven connectors in five families: local files and uploads; the web, in and out; FTP, FTPS and SFTP over the relay; S3 and GCS, with requests signed on your device; Google Drive and Dropbox; sending email; a Telegram bot; outgoing webhooks; an inbox for incoming ones; appending to a spreadsheet; and exporting to Colab. Each is ordinary catalog content — versioned, readable, installed from the catalog like anything else.
What is deliberately *not* there says as much as what is. OneDrive, Slack, Discord, the Gmail API — those are left for people to write: an afternoon of work, with your own app credentials, published like any other content. A missing integration is a gap in a catalog, never a ticket in a vendor's queue. The guide walks the whole path: data & connectors.
The vault the platform cannot read
| Property | Mechanism | Note |
|---|---|---|
| At rest | AES-256-GCM under a device-bound key | Encrypted on your device; the platform holds no copy. |
| Sync & provenance | never | Not synced, not queued to be synced, not written into any record — a rule, not a setting. |
| Backup | a passphrase-protected export file | You hold it. Without it, losing the device means entering the keys again — a design property, not a defect. |
| The one exception | the relay signing in for SSH and FTP | In memory, for that connection only, with your consent — named, never hidden. |
| Sending data out | a separate consent that names the destination | It never rides on the permission you gave for bringing data in; direction is part of the type. |
| Assistants | keys are handled only by you, in the app | The Copilot can set a connector up and show you what it would do; it never sees a secret. |
The secrets a connector needs — API keys, sign-in tokens, SSH keys — live in an encrypted store on your device, under a key bound to that device. They are never synced, never queued to be synced, never stamped into the record of a run. The one copy that exists is yours; the only way a key ever moves is a passphrase-protected export file that you hold and the platform never sees.
There is exactly one exception, and it is named instead of hidden: SSH and FTP require the relay to sign in as you, so that secret passes through the relay — only in memory, only for that connection, and only with your consent each time. It is listed among the platform's honest limits rather than glossed over. Everything else about the trust surface — sandboxing, signatures, what you are asked before what — is on security.
Even *how* a service gets connected is content. Signing in to an outside service is described by content you can write — where its sign-in page is, what permissions it asks for, how it proves the app's identity — so a new provider is content, never engine code. Permissions are held to a minimum when it is written: the built-in Google connector asks only for the individual files you pick, and a provider you write brings its own app credentials, with nobody to ask. The deeper mechanics are in security & privacy.
From foreign bytes to citable datasets
A connector moves bytes; a format converter turns them into typed data you can work with. The built-in set reads and writes ONNX, safetensors, and PyTorch checkpoints; CSV, Parquet, and Arrow; Excel and Word export; and the two common tokenizers, whose vocabularies are versioned artifacts in their own right. Drop a foreign file onto the canvas and it goes through a connector and a converter straight into a dataset — one gesture, and the same consent path as everything else.
Datasets are versioned and addressed by their content — and so are their splits. Train, validation, and test partitions are artifacts referred to by hash, so the exact split behind a result is reproducible rather than folklore. Every dataset carries a datasheet and a profile — columns, missing values, class balance — and every training run records the exact dataset versions it consumed. That is what turns “what data trained this model?” into a query instead of an archaeology project.
Bigger-than-memory is the normal case
Large data is processed as a stream — in chunks, against explicit memory and storage budgets — never by loading everything and hoping. Every block says up front how it will read its data: in a single pass, in a stated number of passes, or by spilling to disk when it will not fit — and the planner holds it to that. Whether a run fits is worked out *before* it starts; one that cannot fit is refused up front, with concrete things you can change. An out-of-memory surprise an hour into a computation is a design defect, not bad luck.
The same discipline holds when nothing is running. Big things are stored in pieces, addressed by their content — streamed rather than re-parsed, fetched when needed, and checked when they arrive. Everything a saved workflow needs is on your device before it runs, so reads come from your own database; a shared dataset arrives as verified pieces, and the world's catalog is queried on demand — never mirrored wholesale onto your disk.
Sends are deliberate; triggers need an open tab
When a workflow acts on the world — sends an email, posts a message, calls a webhook — the act carries duties. Every one of them supports a rehearsal: the exact thing that would go out — where to, the subject, the body, which files — shown to you with nothing actually sent and no consent spent. Sends are also protected against being repeated: a workflow that resumes or re-runs will not accidentally send twice, because your device keeps a record of what it already sent. That record is per-device — honestly — and every send is written into the run's provenance.
Inbound is honest about what a browser is. A tab cannot listen, so incoming webhooks land in an inbox we host — a key you can rotate, size-capped, expiring — which means what comes in is not local-first while it is in transit, and the datasheet says so. An event can start a run only while a tab is open; otherwise it waits in a bounded queue until one is, and if that queue overflows it says so out loud instead of quietly dropping anything.
Arming a trigger is the consent: a standing permission over everything that workflow touches, given by your own gesture at the moment you arm it. A trigger can never create consent when it fires, and an edit that widens what the workflow touches disarms it until you arm it again. The Copilot may draft a trigger; it never arms one. Sends, the inbox, and triggers land through early access in exactly this shape — Scellis is deliberately not a product for unattended automation: a closed tab runs nothing, and a scheduler that would run while your tab is closed is something we have named and not built.