The Hard Part of AI World-Building Isn't Generation — It's Authority
How we built a versioned, permission-scoped world runtime where a Personal AI can propose but never approve. A model can generate a convincing world description in seconds. Turning that description into something a pers
How we built a versioned, permission-scoped world runtime where a Personal AI can propose but never approve.
A model can generate a convincing world description in seconds. Turning that description into something a person can enter, change, share, and derive is a different problem. The hard part is not generation. It is authority: who can change the object, which version they changed, who approved it, and what happens when someone derives from it.
Derive World is an open-source foundation for Living Objects: shared, versioned worlds that a person and their Personal AI can create, change, derive from, and trace through an explicit lineage. v0.2.0 is the second public release and the V2 product stage. This article is a technical look at the model behind it, not a launch announcement.
Real v0.2.0 product in Fixture mode: a newcomer describes a world, approves the plan, enters it, asks the Personal AI to change it, and derives an independent branch. Fixture mode is deterministic demo data; it calls no model and is not real AI. The full recording is also available.
The failure mode of chat-shaped objects
A chat transcript is not, by itself, a versioned object. It typically has no explicit membership model, no permission boundary, no immutable snapshot, and no lineage unless the surrounding system adds them. If the same AI can propose a change and apply it, the system cannot answer a basic audit question: who authorized this state?
We wanted the AI to be a contributor, not an authority. The rule is simple to state and harder to enforce: a Personal AI can propose a change or a derive; only a human can approve it.
One object model, several surfaces
A world is not a page and not a chat thread. It has content, state, variables, permissions, versions and lineage. The same world can run as a time-based 2D live scene, a readable interactive story, an editable workspace, a visual space, uploaded images or video, uploaded or procedural audio, a 3D scene with client-side gravity and collisions, or a bounded declarative custom surface. Tabs can combine several surfaces. All of them share one content, state, variable, permission, version and lineage model.
| Create with your words | Derive an independent child |
|---|---|
![]() |
![]() |
Both screenshots are from the real v0.2.0 product in Fixture mode. Fixture calls no model and is not real AI.
That decision has a cost: every surface has to fit the same versioning and permission rules. It also has a payoff: a world does not become a different product category when its form changes.
Rule 1: one proposal, one version increment
Ask for a change in natural language. A bound Personal AI reads the current world state and version, proposes one mergeable change, and waits. The proposal is inert until a human applies it. Each applied change increments the version exactly once, keeps existing thing ids, survives a refresh, and rejects conflicts instead of overwriting.
Two implementation details matter more than they look:
- State changes require
expected_version. A conflicting write returns409 version_conflict; it does not silently overwrite. The rule lives in the server, not in the model prompt. - The model output is bounded. One model output is capped at 8 KiB, object state at 64 KiB, and one creation confirmation at 12 KiB. The bound forces a proposal to be small enough to review, and it prevents a model from dumping an unreviewable world in one response.
The real-model evidence is deliberately narrow: three consecutive natural-language modifications were recorded on release candidate 4962b9b, each producing one proposal, human approval, exactly one version increment, retained thing ids and refresh persistence. The tagged v0.2.0 tree differs by documentation and release fixes; those flows were not re-run on the exact tag.
Rule 2: the model writes a brief; the server composes the world
A user describes a world in their own words. The Personal AI writes a compact world brief, and a server-side composer expands it into real content, state and experience. The proposal is validated before it is stored, and nothing is created until the human approves it. A new world opens private.
The split is intentional. The model does not write arbitrary runtime code or raw page markup. A free world's content is declarative data; the server interprets every value and never executes user code, loads external URLs, fonts or scripts, or opens network access.
This is also why form extensions are module changes, not per-world pages. All five capabilities are exposed through one Broker; capability registration is configuration-only, with no runtime registration or arbitrary provider URL.
Rule 3: derivation is an immutable snapshot, not a pointer
A derive creates an independent child from an immutable snapshot of the current version at the whole-world level. The parent is not modified, and the child keeps a recorded, permission-clipped ancestry. Later edits to the parent do not propagate. Derivation is not per-entity in v0.2.0.
This is the part that makes provenance useful. A branch is not a mutable view of the parent; it is a new object with its own version history. The ancestry is permission-clipped: objects you cannot read are never named. The trade-off is deliberate — whole-world derivation is simpler to reason about than partial merges.
The architecture around the model
The reference deployment is Docker Compose: a Go service, PostgreSQL with schema 11 (36 tables), and the built web client. The default Compose stack binds to loopback only.
The developer surface is concrete:
- OpenAPI 3.1 contract:
api/openapi/openapi.yaml. - Typed TypeScript SDK with zero runtime dependencies:
sdk/typescript. - Five frozen capabilities:
object.read,object.context,object.propose_change,object.derive,content.structure. - Configuration-only capability registration:
capabilities.yaml.
The permission boundary is enforced on the server. A caller without read permission receives 404, not 403; a public snapshot is a stored, sanitized copy. Public publication is opt-in, and contributor consent is required when other people contributed to the published version.
Provider transport is treated as part of the trust boundary: response signing, verified TLS, HMAC key rotation, per-call model ceilings, per-user daily budgets, and a deployment-wide stop. A tampered or unsigned provider response is treated as an unknown result.
What we deliberately did not build
The limitations are part of the design, not a footnote:
- Single instance. Publication, derivation and social features are scoped to one instance; there is no cross-instance federation, public import or horizontal scaling.
- No simultaneous co-editing. The object workspace uses an authenticated SSE refresh signal with a polling fallback; version conflicts are reported instead of silently overwriting.
- Bounded forms. World content is declarative 2D and 3D surfaces with uploaded media and simple client physics; it has no network access, arbitrary code, live multiplayer sync, or arbitrary video generation.
-
Real AI is scoped. Real-model evidence is limited to documented flows on DeepSeek
deepseek-flash; the release makes no general provider claim. Fixture mode is deterministic demo data, calls no model, and is not real AI. - Not production. This is a self-hostable OSS MVP, not a production platform.
If you are evaluating the runtime for a hardened deployment, those boundaries should be the first thing you test.
What I would inspect first
-
README.md— the supported Compose path and the Fixture and real-model mode distinction; Fixture is not real AI. -
SHOWCASE.md— the Create → Experience → Change → Derive → Share loop. -
docs/en/limitations.md— the exact caps and the honest scope. -
api/openapi/openapi.yaml— the OpenAPI 3.1 contract. -
capabilities.yaml— the frozen capability registration. -
sdk/typescript— the typed SDK.
Run it locally in Fixture mode:
git clone https://github.com/QiyanAI/derive-world.git
cd derive-world
cp .env.example .env
docker compose up --build -d
Open http://127.0.0.1:8080, register a local account, describe a world, approve the plan, enter it, ask for one change, and derive a branch. Fixture mode needs no model account.
The online product tour is a static page built from real v0.2.0 Fixture screenshots with a clearly labelled simulation; it is not a live AI service.
- Repository: github.com/QiyanAI/derive-world
- Showcase: SHOWCASE.md
- Full recording (Fixture): demo-v02-17s.webm
If you have built a versioned AI object or a derivation system, I would like to compare notes on the approval boundary. The interesting question is not whether the model can generate a world. It is whether you can explain, a week later, who changed it and why.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.

