34 lines of JSON define an entire encrypted application. RBAC, encryption, UI, and E2E tests — all derived from a single declaration.
An app.enc.json file is a minimal JSON declaration that a transpiler reads to derive all Lean spec blocks for a complete ENC application. Four top-level fields — data, ui, test, and encryption — define the event schema, RBAC rules, screen layout, and end-to-end test plan. The transpiler (gen_from_enc.py) produces Lean spec blocks that are byte-identical to the hand-written originals.
Here is the complete Chat application — 34 lines:
{
"app": "Chat",
"encryption": "mls",
"color": "#10b981",
"data": {
"Message": "Member CR",
"Reaction": "Member C",
"Profile": "Member C",
"Topic": "Admin C",
"Read": "Member C",
"Grant": "Owner C -> Admin Member",
"Revoke": "Owner C -> Admin Member"
},
"ui": [
"groups | tab default | list groups",
"compose | tab | form group-name member-key -> create",
"group/:id | | thread messages -> send Message",
"profile | tab | form display-name bio -> save Profile"
],
"test": [
"alice creates group 'Alices Group' with [bob, charlie]",
"alice creates group 'Bobs Group' with [bob]",
"alice sends 'Hello everyone!' in 'Alices Group'",
"bob sends 'Hey Alice! Bob here.' in 'Alices Group'",
"charlie sends 'Charlie checking in!' in 'Alices Group'",
"alice sends 'Welcome!' in 'Bobs Group'",
"bob sends 'Thanks!' in 'Bobs Group'",
"---",
"alice sees 2 groups",
"alice opens 'Alices Group' sees ['Hello everyone', 'Bob here', 'Charlie']",
"alice sends 'From UI!' in 'Alices Group'",
"alice opens 'Bobs Group' sees ['Welcome', 'Thanks']",
"alice sends 'Also from UI!' in 'Bobs Group'",
"alice opens groups sees 2 groups",
"alice opens profile",
"alice cycles [groups, compose, profile]"
]
}
From this single file, the pipeline derives the RBAC schema, role bitmasks, manifest constructor, content validators, frontend routes, mobile screens, service SDK, test contracts, and E2E test flows — across Lean, TypeScript, React, and React Native.
| Field | Type | Required | Description |
|---|---|---|---|
app | string | yes | Display name ("Chat", "DM", "Timeline") |
encryption | string | yes | "none" | "ecdh" | "mls" |
color | string | yes | Primary color hex ("#10b981") |
data | object | yes | Event type to role shorthand map (the RBAC schema) |
ui | string[] | yes | Pipe-delimited screen definitions |
test | string[] | yes | Natural language test steps, "---" separates setup from validation |
Optional fields include roles (explicit role list), endpoints (dev server ports), react (React vs vanilla DOM), and includes (composition from other apps).
The "data" field maps event type names to role shorthand strings. Each entry compiles to an RBAC schema rule with event type, role, and permitted operations.
// Basic: role + operations
"EventType": "Role Ops"
// With targets: for Grant/Revoke
"EventType": "Role Ops -> TargetRole1 TargetRole2"
| Shorthand | Expanded |
|---|---|
"Member CR" | {event: "Message", role: "Member", ops: ["C", "R"]} |
"Owner C -> Admin Member" | {event: "Grant", role: "Owner", ops: ["C"], target_roles: ["Admin", "Member"]} |
"Self CR" | {event: "Sent", role: "Self", ops: ["C", "R"]} |
"Any R" | {event: "*", role: "Any", ops: ["R"]} |
Valid operations: C (Create), R (Read), U (Update), D (Delete), P (Push).
| Name | Meaning |
|---|---|
* | Wildcard — applies to all event types |
Grant | Role grant operation (has target_roles) |
Revoke | Role revoke operation (has target_roles) |
Grant_Push | Grant + webhook push registration (has target_roles) |
| Role | Meaning |
|---|---|
Self | Author of the event (own events only) |
Any | Any identity (no role required) |
Owner | Enclave owner |
DataView | Data view service role |
The "ui" array defines screens as pipe-delimited strings. Each line produces a route, a view file, a mount function, testIDs, and both web and mobile screen shells.
route | [tab [default]] | elements
| Element | Syntax | Produces |
|---|---|---|
| List | list X | FlatList/scroll with testID X |
| Form | form field1 field2 -> action | TextInput(s) + submit button |
| Thread | thread X -> send EventType | Message list + compose input + send button |
| Toggle | toggle [a, b] | Segmented control switching between views |
| Show | show field | Read-only display field |
| Detail | detail field1 field2 | Detail view with labeled fields |
| Actions | actions like comment | Action buttons (appended to detail) |
// Tab with default list view
"groups | tab default | list groups"
// Form with two fields and a submit action
"compose | tab | form group-name member-key -> create"
// Thread view (messages + send)
"group/:id | | thread messages -> send Message"
// Search with toggle
"search | tab | form search | toggle [nodes, identities]"
// Detail with action buttons
"post/:id | | detail post comments | actions like comment"
// Settings (read-only display)
"settings | tab | show dataview-url"
From each screen key the transpiler auto-derives:
"groups" stays "groups", "group/:id" becomes "group")"mount" + PascalCase ("mountGroups", "mountGroup")tab screens)list:groups, input:group-name, btn:create)The "test" array contains natural language steps that compile into structured E2E test flows. A "---" separator divides SDK-level setup from UI-level validation.
"test": [
// ── SDK setup (runs via SDK, no UI) ──
"alice creates group 'Alices Group' with [bob, charlie]",
"alice sends 'Hello everyone!' in 'Alices Group'",
"bob sends 'Hey Alice!' in 'Alices Group'",
"---",
// ── UI validation (runs against rendered app) ──
"alice sees 2 groups",
"alice opens 'Alices Group' sees ['Hello everyone', 'Hey Alice']",
"alice cycles [groups, compose, profile]"
]
| Pattern | Action |
|---|---|
alice creates group 'X' with [bob, charlie] | createGroup |
alice sends 'Hello!' in 'X' | sendGroupMessage |
alice sends 'Hello!' to bob | sendDM |
alice requests bob | sendRequest |
bob accepts alice | acceptRequest |
alice posts 'Hello world!' | post |
alice likes bob post | like |
alice comments 'Great!' on bob post | comment |
alice follows bob | follow |
alice sets profile name='Alice' bio='Tester' | setProfile |
sdk registers N nodes | sdkRegister |
| Pattern | Action |
|---|---|
alice sees N groups | assertGroupCount |
alice opens 'X' sees ['a', 'b'] | openAndValidate |
alice sends 'X' in 'Y' via UI | sendGroupMessage (UI) |
alice opens profile sees 'Alice' | openScreenAndValidate |
alice opens friends sees [bob, charlie] | openScreenAndValidate |
alice cycles [tab1, tab2, tab3] | navCycle |
alice opens node detail sees [pubkey, endpoint] | openDetailAndValidate |
alice opens search searches node | searchFor |
alice registers node via UI | registerViaUI |
The "encryption" field selects one of three levels, each adding cryptographic capabilities to the generated wallet, session, and middleware:
| Level | Value | Wallet | Session | Middleware |
|---|---|---|---|---|
| L0 | "none" | Extension + devmode, simple signing | SHA256 challenge | None |
| L1 | "ecdh" | + persistent ECDH keypair, local signing | BIP-340 taggedHash, eager | ECDH key exchange |
| L2 | "mls" | + grant tracking, identity session | BIP-340 taggedHash, deferred | MLS group key management |
"encryption" field changes.The transpiler generates six Lean spec blocks from each app.enc.json:
Web route definitions, endpoint URLs, middleware type. Drives config.js, router.js, wallet.js.
Expo Router tabs, screens, onboarding config. Drives the React Native app scaffold.
SDK operations, signing config, identity storage. Drives the service module shared by web and mobile.
Per-screen element contracts — testIDs for lists, inputs, buttons, text. Auto-derived from ui.
Structured E2E test actions parsed from natural language test steps.
RBAC schema, role bitmasks, manifest constructor, content validators. The data backbone.
The JSON definition flows through a multi-stage pipeline, producing artifacts at each layer:
The amplification ratio is dramatic:
Layer Lines Ratio
----- ----- -----
JSON input 34 1
Lean specs 306 9
TypeScript out 23,222 683
-----
1 : 9 : 683
34 lines of JSON produce 306 lines of Lean spec which generate 23,222 lines of TypeScript — screens, service modules, test contracts, and E2E workflows. The developer writes JSON. The pipeline writes everything else.
Five applications are defined with app.enc.json, spanning all three encryption levels:
| App | Encryption | Color | Data Events | UI Screens | Test Steps |
|---|---|---|---|---|---|
| Registry | none | #5b5bd6 |
4 | 6 | 11 |
| Timeline | none | #1d9bf0 |
8 | 7 | 13 |
| DM | ecdh | #10b981 |
7 | 5 | 14 |
| Chat | mls | #10b981 |
7 | 4 | 16 |
| Messenger | mls | #50e3c2 |
composed | 12 | 19 |
"includes" field. Its data schema is merged from the constituent apps; routes, tests, and service blocks are defined independently.ENC PROTOCOL · APP DSL · 34 LINES · 5 APPS · 1:683 RATIO