Stellium
A TypeScript framework where the schema lives inside the data graph it describes
Stellium is a TypeScript framework I’m building because I want my data and my schema to be the same thing. Type definitions, field definitions, and the edges that compose them are all just nodes in the same graph as the records they describe. You query your schema with the same primitives you query your data with. There’s no separate metadata channel, no privileged migration system that lives outside the graph it modifies — the model knows what it is, and you can ask it.
That commitment is the thing every other Stellium design choice falls out of.
Why this matters to me
Most data frameworks force a choice. On one side: a rigid relational schema that’s correct and validated and a nightmare to evolve when requirements shift. On the other: a document store or graph database that lets you change anything but gives you no guardrails when your model drifts away from itself.
I want both. I want a model I can mutate with the same fluency as the data, with the same query language, and with the option to dial in strictness when the application calls for it. Stellium is my attempt at that middle ground.
The deeper reason: I think the next generation of software is going to be assembled rather than coded. For that to actually work, an LLM needs to be able to read the schema, understand it, modify it, and write code against it — all without a parallel “the schema lives over here, the data lives over there” coordination layer. If the schema is just more graph data, an agent can discover it the same way it discovers anything else. The same thesis drives SCEpter; Stellium is the data layer side of it.
The core ideas
Composition, never inheritance. A node isn’t “a Document” or “a Task” — it can be both at the same time. Types are bags of field references that attach independently. You add and remove types at runtime. This is closer to Rust traits or Go interface embedding than to OO class hierarchies, but applied at the data layer rather than the compile-time type system.
Fields are first-class. A field isn’t just a column name. It’s a reusable definition with type constraints, validation rules, and metadata. The same title or createdAt field can appear in many types — defined once, referenced everywhere.
The schema is in the graph. Type definitions, field definitions, and the edges between them are nodes you can query, traverse, and reason about. Migrations stop being one-off scripts and become diff-and-reconcile operations against two states of the same model.
Pluggable backends. The same Stellium codebase runs against SQL (the suggested default), Neo4j, an in-memory JSON store for tests, or a markdown filesystem for Zettelkasten-style notes. The Engine layer is selected from a config flag — you swap backends without touching application code.
The strictness dial
This is the feature that’s hardest to find elsewhere, and it’s the thing I’d point to if I had thirty seconds to explain why Stellium isn’t just another graph framework.
Stellium has behavioural flags per-schema and per-type — enforceRequiredFields, allowBorrowingFields, noShadowing, sealed. At one setting it behaves like Notion: flexible, forgiving, fields borrowed across types, validation deferred. At the other it behaves like a strict production ORM: required fields enforced, composition validated, no implicit shadowing. Same data, same code, configuration change instead of migration.
You can prototype loose and tighten as the model stabilises. You can keep a personal notebook in the same codebase as a multi-tenant API. The dial is the affordance that makes “same framework, very different applications” actually viable, rather than a marketing claim.
What this is, right now
Pre-1.0. Version 0.0.0. Active development. The four backends (SQL, Neo4j, in-memory JSON, markdown filesystem) are all implemented. The schema-as-graph DSL works. The strictness flags are real. The MCP server exposes the same operations to AI agents so a tool-using model can introspect and mutate the graph the same way application code does.
Larger systems described in the docs — the full mutation pipeline with CRDT support, replication, undo, and audit; the schema reconciliation engine; federation; the Intent presentation layer — are designed but only partially built. The README itself is candid about this, and notes that most prose in the repo is AI-generated and prone to overstatement. The code is ground truth; the marketing isn’t.
The intended user right now is me — building personal-software-as-platform on infrastructure I own — and other TypeScript developers with relationship-heavy data who’d otherwise reach for Prisma or Supabase and want to keep their options open about what’s underneath.