Skip to content

Project status

Status as of September 2026: MatterData is an engine MVP under active development. It can compile and serve substantial fixture applications, but it is not ready for production tenants.

  • production parser, semantic checks, canonical model, and generated JSON Schema.
  • generated SQLite DDL, schema introspection, migration differ, and destructive-change guard.
  • tenant resolution, authentication, sessions, lockout, and default-deny authorization.
  • server-rendered navigation, reports, lists, details, forms, statistics, notes, Markdown, and read-only matrices. A menu of five links or fewer is one row; a larger one is a menu per nav group that opens on pointer or keyboard, and a full-screen menu below 48rem. Neither needs JavaScript. See Navigation.
  • typed validation, transactional insert, update, delete, and lifecycle transitions.
  • form fields that the browser improves and that work without JavaScript: show_when: hides a field, suggest: offers an end time, add_via: creates a missing option in a dialog, and quick_picks: puts a value such as now into a field with one press. See Regions.
  • app-wide scopes and scoped routes with up to three row parameters.
  • sandboxed before/after and form-save hooks.
  • re-encoded image uploads with authorized blob delivery and per-tenant quota accounting.
  • the editable sheet region. With JavaScript it is an editing grid: range selection, copy and paste against Excel, undo, fill, and one save that changes, removes, and creates rows in one transaction (actions: save, add, delete). Without JavaScript, and on a phone, it is a read-only list whose rows link to the form named by row ->. See Regions.
  • CSV export and recurring ICS calendar subscriptions.
  • the organization’s administration page at /matterdata: create a login, disable and enable one, set a new password, end every session, and set or clear the tenant administrator flag.
  • the job log at /matterdata/jobs, which shows every scheduled occurrence and marks one due. The page executes nothing itself.
  • the usage page at /matterdata/usage: records per table, the database file’s size, the bytes of the images records still hold against the storage quota, replaced images that the nightly job has not yet removed, live sessions, the last sign-in, and how many audit records of each class the organization holds. The quota is shown and not changed there.
  • the audit log at /matterdata/audit: every recorded event, filtered by class, login, and date. Opening the page is itself a recorded event, because the log names who read which records and an administrator who could browse it invisibly would be the one person whose reading leaves no trace.
  • the application list at /matterdata/apps: the name, the address, the version, the model schema, the checksum, and when each one was installed. It also names an application whose stored model the engine could not read, with the reason. The page is read-only; installing and rolling back belong to the deploy command.
  • an organization keeps serving when one of its applications cannot be read. The other applications answer as usual, and so do the login page and the administration page. The broken one answers 503 at its own addresses, and only a tenant administrator is told why, because the reason names tables. An organization with no application at all opens too, which is what one looks like before its first application is installed.
  • retention on a table, enforced nightly in batches of 1,000 records, with the removal of images that no record refers to any more.
  • versioned columns. A versioned column keeps a version at every save, up to 20 per record by default. A versions region lists them, compares two of them line by line, and restores one through the page’s own form. See Keep the history of a column.
  • the nightly reset of a demonstration organization to a stored template, from the administration page or from install -reset-nightly.
  • per-application and per-page time zones, with a reader-selected time zone for a page that declares timezone: user.
  • several applications in one organization. Each one is served under its own first path segment, which mount: declares and which defaults to the application’s name. One application can take the organization’s root with mount: /. A table is stored as <application>__<table>, so two applications can both declare events. See App mount.
  • one login with a role per application. authenticated admits a login that holds a role in this application. A login with no role here is anonymous here, and is still named in the audit trail. See An organization, its applications and one login.
  • install against an organization that already exists adds an application to it, and can give an existing login a role in the new application. See Add a second application to an organization.
  • the list of applications at the organization’s root, which redirects when there is only one.
  • four compiling fixture apps: club, newspaper, school, and calendar.

Administrators can assign roles per application and create copyable invitation links. Recipients choose their own password through a single-use link valid for 7 days. See Manage logins.

  • matterdata validate, hot-reloading dev, atomic deploy, and rollback. An application that is already installed cannot be changed: install refuses it and names deploy, which holds the destructive-change guard and the rollback. Re-create the organization while you develop.
  • audit records for deploys and destructive migrations, which wait for the deployment commands. Everything else the audit log covers is recorded, including reads and writes of sensitive columns — see Security model for the classes and their retentions. A hook’s own reads are outside the log.
  • a duration in any expression but suggest:. 30min, 1h and 7d are literals of the language, and a form field’s suggest: is the one place an expression takes one — see Regions. In a check, a where:, a value: or a var, a duration is a compile error that says so and names suggest:. Adding a duration to a date or a datetime is not expressible anywhere.
  • nested navigation. A nav block is one level deep; a group cannot hold a group. A page that belongs under a heading inside a group has no way to say so, so an application with many reference tables lists them beside the pages people use every day. See Navigation.
  • a picture column in a sheet. An img column in columns: compiles and is refused when the tenant is opened. Edit a picture in a form on the row’s own page.
  • a sheet over rows that do not exist yet. A sheet edits and creates rows of one table. The school’s grade screen needs rows that come from the enrolments, and a save that inserts on a key. Neither is built.
  • the script of an application’s own job. The declaration compiles, the engine gives it a schedule and a clock, and the job appears in the job log. Nothing runs the script: the runtime has no executor for an application-declared job. Every occurrence is claimed, recorded as a failure naming the missing half, and its clock advances. Do not ship a job as work that happens.
  • two parts of the administration page at /matterdata:
    • pausing a job. It needs a decision on whether a paused job accumulates occurrences. Today a job either has a schedule or does not.
    • deleting a login. Disabling one is the act that exists. A disabled login cannot sign in, and its live sessions are rejected on their next request.
  • moving between applications inside the page. The page frame draws no switcher. A person reaches another application through the organization’s root or through a bookmark. An organization that has a root application and other applications has no engine page that lists the others at all; the engine logs that when it opens the organization.
  • a root-relative link in Markdown stays root-relative. [Members](/members) written in a markdown column is served unchanged, so in an application under a mount it leaves that application. Write the link relative to the current page instead.
  • binding a profile when an application is added. install -profile is refused for an application added to an organization that exists, because a profile row is written only when a login is created.
  • a confirmation mail to a visitor. A hook on a write by a visitor with no login may mail only the organization’s own logins, because the visitor’s address is whatever they typed. Confirming that address needs double opt-in, and double opt-in needs mail delivery. See Add logic.
  • a multi field on a public form that lists only public rows. Its boxes show up to 200 rows of the referenced table to every visitor. A ref field there is refused. See Regions.
  • a password reset that a person performs for themselves. This build has no SMTP, so an administrator sets a new password and hands it over.
  • production provisioning, backup and restore automation, monitoring, and restore drills.
  • full TypeScript checking and bundling for hook modules.
  • complete gone semantics for archived records.
  • four parts of versioned columns: a word-level comparison, a restore in one press, a sensitive or img column in a history, and a history that survives deleting its record. A hook cannot read a history either.
  • comparing a record’s pending revision with its live text. The newspaper keeps a revised title and body beside the published ones. A page can show both, and cannot show the difference between two columns of one record: there is no declaration for it yet.
  • a timezone: on a single column. A time zone is a property of the application and of the page today, so one page draws every datetime in one time zone.
  • a recurring calendar series that keeps a time zone of its own, separate from the application’s.
  • screen-reader evidence for the sheet’s grid. No device has been measured. NVDA with Firefox, VoiceOver with Safari, a keyboard layout other than German, and a coarse-pointer layout are all still owed. The keyboard focus is on a hidden text field beside the grid, not on the grid cells, and that choice is part of what the measurement must judge. Do not treat the grid as accessibility-tested. Until the evidence exists, the grid is shown on the demo and showcase applications and is not turned on for a customer organization. A reader who cannot use the grid edits one row at a time through the sheet’s row -> link.
  • frozen columns in a sheet. The grid can keep columns in place while the reader scrolls sideways, but no declaration says which columns. A sheet freezes no column today.
  • a search in a sheet’s ref dropdown. A ref dropdown in the grid offers at most 200 rows of the target table, the same limit as a form’s dropdown. A row past that limit cannot be chosen or pasted.
  • a column’s help: in a list. A report does not show a column’s help: text, and a sheet read without JavaScript is a report, so it does not show it either. The sheet’s grid and the form on the row -> page show it.
  • a manual accessibility audit. An automated scan (axe-core, WCAG 2.1 levels A and AA) checks every page of the two test applications in Chromium and WebKit, in the light and the dark scheme. On 2026-09-23 it found no violation on 62 pages. The scan finds missing labels, bad ARIA and low contrast. It cannot tell if a page is usable with a screen reader.
  • the remaining form enhancement work.
  • pilots and the outstanding Phase 0 validation with real organizations and outside DSL readers.

The fixtures are more than syntax samples: the test suite compiles them and exercises their pages, writes, permissions, and exports. They demonstrate that the current model can express meaningful applications. They do not establish operational readiness, product-market fit, or long-term sandbox isolation.

Follow the repository’s ACTION-PLAN.md for the detailed engineering plan. Internal decision records explain the trade-offs behind the implementation; this public site will gradually turn stable parts of that material into task-oriented reference documentation.