Skip to content

Core Specification (v0.1)

This specification defines the physical layout, manifest schema, Content Security Policy, and database geometry of DAI containers.

1. Container Distribution Layout

A DAI container may be distributed as a standalone binary archive (.dai) or as an executable polyglot HTML document (.dai.html):

[app-name].dai.html
├── <meta name="dai-integrity" content="required">
├── <meta name="dai-public-key" content="<base64 SPKI>">
├── <script id="dai-bootloader">   inlined runtime bootloader
└── <script id="dai-payload">      base64 of ZIP archive:
    ├── app/**                     compiled application assets
    ├── runtime/sqlite3.wasm       bundled SQLite engine (865 KB)
    ├── runtime/sqlite3.mjs        Emscripten WebAssembly glue (579 KB)
    ├── runtime/container.html     sealed copy of the container shell
    ├── runtime/manifest.json      manifest, hashes, and signature block
    └── document.sqlite            stateful database

2. Content Security Policy (CSP)

The following CSP header/meta-tag is strictly enforced:

http
default-src 'none';
script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' blob:;
style-src 'self' 'unsafe-inline' blob:;
img-src 'self' data: blob:;
font-src 'self' data: blob:;
media-src 'self' data: blob:;
frame-src 'self' blob:;
worker-src blob:;
connect-src 'none';
form-action 'none';
base-uri 'none';
object-src 'none';
  • connect-src 'none' is absolute and non-negotiable.
  • blob: is permitted for scripts, styles, images, and frames because blob URLs are origin-local object URLs minted directly from the embedded payload in memory.
  • 'unsafe-eval' is prohibited; only 'wasm-unsafe-eval' is granted for SQLite instantiation.

3. Formal Manifest Schema (runtime/manifest.json)

json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": [
    "manifestVersion",
    "documentUuid",
    "appName",
    "createdAt",
    "algorithm",
    "integrityPolicy",
    "hashes"
  ],
  "properties": {
    "manifestVersion": { "type": "integer", "const": 1 },
    "documentUuid": { "type": "string", "format": "uuid" },
    "appName": { "type": "string" },
    "favicon": { "type": "string" },
    "createdAt": { "type": "string", "format": "date-time" },
    "algorithm": { "type": "string", "enum": ["SHA-256"] },
    "integrityPolicy": { "type": "string", "enum": ["required", "advisory"] },
    "hashes": {
      "type": "object",
      "additionalProperties": { "type": "string" }
    },
    "validUntil": {
      "type": "integer",
      "description": "Optional Unix timestamp after which execution is refused."
    },
    "signatureAlgorithm": { "type": "string", "enum": ["ECDSA-P256-SHA256"] },
    "publicKeyFingerprint": { "type": "string" },
    "signedEntries": {
      "type": "object",
      "additionalProperties": { "type": "string" }
    },
    "signature": { "type": "string" }
  }
}

4. The Canonical Payload

Signatures are computed over the canonical payload string generated by canonicalPayload():

typescript
export function canonicalPayload(
  uuid: string,
  entries: Record<string, string>,
  validUntil?: number,
): string {
  const sorted = Object.keys(entries)
    .sort()
    .map((name) => name + ":" + entries[name])
    .join("\n");
  const base = "dai-v1\n" + uuid + "\n" + sorted + "\n";

  // Appended only when present so unsigned/perpetual containers remain backward compatible
  return validUntil === undefined ? base : base + "!validUntil:" + validUntil + "\n";
}

Why validUntil is Covered by the Signature

No other manifest field is signed—the manifest is excluded from its own digest list. If validUntil were an unsigned manifest property, an adversary could modify or delete the timestamp using a text editor. By appending !validUntil:<timestamp> to the canonical signed payload, modifying or extending the expiration invalidates the cryptographic signature.


5. Database Geometry (SQLite)

  • Pinned 4096-byte Page Size: All newly initialized databases execute PRAGMA page_size = 4096 before table creation. This ensures deterministic database geometry across varying SQLite engine builds.
  • In-Memory VFS: Databases are deserialized into memory using sqlite3_deserialize and exported via sqlite3_js_db_export.
  • Database Excluded from Signature: document.sqlite is hashed in manifest.hashes but excluded from manifest.signedEntries. The application logic remains permanently signed and authentic while user data can evolve across saves.

Released under the MIT License. Dynamic Application Interface standard.