opbox

The import console is how a spreadsheet of data becomes typed rows in an Opbox table without a single risky bulk write. You map your columns onto the table, dry-run the whole thing to see exactly what would happen, and only then commit. Nothing is written until you have seen the outcome row by row, and the commit itself is bound to a person, so a bulk load of regulated data is always a deliberate human act on the record, never something an agent slips in.

What it does

A dry-run that shows you the exact outcome first. Point a set of incoming rows at a target table and the preview classifies each one as create, update, or reject against the table’s real schema, with a per-cell result for every value. You see the summary - how many rows would be created, how many updated, how many rejected - and the reason behind every rejection, before anything touches the store. The preview writes nothing canonical: it is a projection of what the commit would do, so the shape you review is the shape you get.

Per-cell type coercion you can check up front. Each cell in the preview is checked against the type of the column it lands in. A value that fits is marked good, a value that does not is flagged as a coercion warning, and the column’s type travels with the result, so a date that arrived as free text or a number with stray characters surfaces in the preview rather than as a surprise after the load. Cells aimed at a column that does not exist, at a computed column (which has no stored value to write), or at a reserved key are rejected with the reason named.

Upsert by a match key. Name a column as the match key and the import becomes an upsert: a row whose match value already exists in the table is classified as an update, and a row whose value is new is a create. Leave the match key out and every row is a create. The same match logic that drives the preview’s classification drives the commit, so the create-versus-update split you reviewed is the split that happens.

Commit is one governed write, atomic, and human-attributed. When you commit, the import runs through the kernel’s single bulk-write path rather than a separate import engine, so it inherits all the same guards: a bounded batch ceiling, prototype-pollution fencing, per-column permission checks, and the rejection of any write to a computed column. The whole import is atomic on the dispatch transaction: one hard row error rolls the entire load back, so you never end up with a half-import. The commit is refused for any caller that is not a person, which means a caged agent can never drive a bulk import of regulated data, and it lands as exactly one audit event attributed to the human who ran it.

Locked cells and regulated data are handled at the write. A cell that has been locked is skipped rather than clobbered (the merge-with-locks behaviour is on by default), so an import cannot overwrite a value someone has deliberately pinned. Cells in a column marked as regulated are encrypted at the verb on the way in, by the same path the kernel uses for any other write, so no plaintext regulated value is ever stored by an import.

It builds on the table you import into. The console imports into a table you have already shaped: a categorised, typed table with the columns the data will land in. You author the table, add and type its columns (including computed columns, which the import will correctly refuse to write to), and the import reads that schema to know how to classify and coerce each incoming cell.

How you use it

Shape the destination table. Create the table you want to load into and add the columns the data needs, each with its type. The import reads this schema, so the columns you define are what the incoming cells are matched and coerced against.

Preview the import. Map your CSV or pasted rows onto the table’s columns and run a preview. Read the summary and walk the classified rows: see which would be created, which updated, and which rejected, and for any rejection read the reason - an unknown column, a computed column, or a value that will not coerce. Adjust the mapping or the source data and preview again until the outcome is what you want.

Choose how rows match. If you are updating existing rows, name the match-key column so the preview upserts: existing match values become updates, new ones become creates. Leave it out and every row is a fresh create.

Commit it. Once the preview is clean, commit. The rows are written through the one bulk path - bounded, atomic, lock-aware, and encrypting any regulated cell - and the load is recorded as a single audit event attributed to you. If any row fails hard, the whole import rolls back and nothing is left half-applied.

The kernel verbs behind it

Everything above runs through the kernel’s one front door: each call is permission-checked and audited before it executes. These are the key verbs.

The import itself:

  • import.preview - dry-run a CSV or pasted column-map against a target table: per-row create/update/reject classification and a per-cell coercion result, writing nothing.
  • import.commit - commit a previewed import (create or update rows per the column-map and match-key), as a human-attributed act delegating to the one bulk-write path.

The table and columns you import into:

  • table.create - author a categorised, typed table.
  • table.get - read a table and its schema.
  • table.archive - hide a table reversibly, keeping its rows.
  • table.delete - tear a table down, with its edge fate made explicit.
  • column.add - add a typed or computed column (computed columns are never writable).
  • column.list - read a table’s columns, with computed and regulated columns flagged.
  • column.retype - change a column’s storage type without dropping data.

The underlying row writes:

  • row.bulk - the bounded bulk create/update path the commit delegates to.
  • row.create - create a single typed row.
  • row.patch - a partial field write that honours cell locks and rejects computed-column writes.
  • row.list - list a table’s rows for the inline-edit grid.
  • row.get - read one row.
  • row.delete - delete one row.

See the full set in the table verb reference, column verb reference, and row verb reference.