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.
Available in the repository
Section titled “Available in the repository”- 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
navgroup 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, andquick_picks:puts a value such asnowinto 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
sheetregion. 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 byrow ->. 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.
retentionon 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
versionedcolumn keeps a version at every save, up to 20 per record by default. Aversionsregion 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 withmount: /. A table is stored as<application>__<table>, so two applications can both declareevents. See App mount. - one login with a role per application.
authenticatedadmits 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. installagainst 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.
Important unfinished work
Section titled “Important unfinished work”matterdata validate, hot-reloadingdev, atomicdeploy, and rollback. An application that is already installed cannot be changed:installrefuses it and namesdeploy, 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
sensitivecolumns — 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,1hand7dare literals of the language, and a form field’ssuggest:is the one place an expression takes one — see Regions. In acheck, awhere:, avalue:or avar, a duration is a compile error that says so and namessuggest:. Adding a duration to adateor adatetimeis not expressible anywhere. - nested navigation. A
navblock 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. Animgcolumn incolumns:compiles and is refused when the tenant is opened. Edit a picture in aformon the row’s own page. - a
sheetover 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 ajobas 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 amarkdowncolumn 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 -profileis 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
multifield on a public form that lists only public rows. Its boxes show up to 200 rows of the referenced table to every visitor. Areffield 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
gonesemantics for archived records. - four parts of versioned columns: a word-level comparison, a restore in one press, a
sensitiveorimgcolumn 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 everydatetimein 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
refdropdown. Arefdropdown 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’shelp: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 therow ->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.
What the examples prove
Section titled “What the examples prove”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.