Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
`compute.hosted_api_used: true` requires at least one entry in
`compute.hosted_api_providers[]`. [#101]
- `tools/test_genesis_schema.py` — 36 sanity-test cases (4 happy-path
+ 32 negative) wired into `tools/batch_validate.py`. [#101]

Check failure on line 41 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Unordered list style

CHANGELOG.md:41:1 MD004/ul-style Unordered list style [Expected: dash; Actual: plus] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md004.md
- `docs/LIFE_LIFECYCLE_SPEC.md` — per-topic normative spec for Topic 2
(Asset Lifecycle). Defines four document shapes
(`package_lifecycle`, `asset_lifecycle`, `mutation_event`,
Expand Down Expand Up @@ -80,18 +80,34 @@
non-`x-` keys reject statically (decision D4=C fail-close at schema
layer). [#103]
- `tools/test_binding_schema.py` — 63 sanity-test cases (11 happy-path
+ 52 negative) wired into `tools/batch_validate.py`. The 63 includes

Check failure on line 83 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Unordered list style

CHANGELOG.md:83:1 MD004/ul-style Unordered list style [Expected: dash; Actual: plus] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md004.md
three negatives for `providers_whitelist_ref` path-traversal (added
in #111 review fix-up) and eight more cases (6 negative + 2 happy)
for path-traversal rejection on `surface.ui_hints.avatar_image_ref`
and `surface.ui_hints.background_audio_ref`, applying the same
cross-schema convention. [#103]
- `docs/LIFE_RUNTIME_STANDARD.md` — appends Part B with normative
v0.8 additions for Topic 4 (Runtime / Assembly): the five-stage
assembly pipeline (Verify / Resolve / Assemble / Run / Guard),
the Provider Registry concept, the abstract
`LifeCapabilityProvider` interface, the three-tier sandbox class
(`built_in` / `user_installed` / `bundled_in_life`), the
hosted-API AND-gate, and the OS-package-manager bootstrap rule.
Encodes Topic 4 decisions D1=C (graded sandbox), D2=B (no
bundled providers in v0.8), D3=mixed (offline + hosted both
first-class), D4=C (three-field surface — already in binding
spec), D5=C (OS package manager bootstrap), and the new D6
(fail-close stage gating). Adds four new audit event types:
`capability_bound`, `assembly_aborted`, `withdrawal_check`, and
`lifecycle_transition_observed`. Part A (the v0.7 eight-step
load sequence) is unchanged. [#105]

[#101]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/101
[#102]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/102
[#103]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/103
[#105]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/105


Check failure on line 110 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Multiple consecutive blank lines

CHANGELOG.md:110 MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md012.md
## v0.7-vision-shift (2026-04-26)

**Status**: Released. Repositions DLRS's ULTIMATE from "Digital Life
Expand All @@ -99,7 +115,7 @@
to "**`.life` 可运行数字生命档案文件标准**" — a dual standard:

1. **`.life` archive file format** — the distribution unit, a packaged
+ signed subset of a DLRS v0.6 record.

Check failure on line 118 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Unordered list style

CHANGELOG.md:118:1 MD004/ul-style Unordered list style [Expected: dash; Actual: plus] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md004.md
2. **`.life` runtime protocol** — how compatible runtimes load + execute
a `.life` to produce an *AI digital life instance*.

Expand All @@ -112,7 +128,7 @@
under milestone
[`.life Archive + Runtime Standard (v0.7-vision-shift)`](https://github.com/Digital-Life-Repository-Standard/DLRS/milestone/5).
All 8 sub-issues #80–#87 closed; PRs #88, #89, #91, #92, #93, #94,
#95, #97, #98 merged.

Check failure on line 131 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

No space after hash on atx style heading

CHANGELOG.md:131:1 MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#95, #97, #98 merged."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md018.md

This epic ships **specs + schema + example builder**. It does **not**
ship a working runtime — that is deferred to v0.8+.
Expand Down Expand Up @@ -426,7 +442,7 @@

### Closes

#28 (epic), #29, #30, #31, #32, #33, #34, #35, #36, #37, #38.

Check failure on line 445 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

No space after hash on atx style heading

CHANGELOG.md:445:1 MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#28 (epic), #29, #30, #31, #32..."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md018.md

---

Expand Down Expand Up @@ -480,7 +496,7 @@

### Closes

#17, #18, #19, #20, #21, #22, #23, #24, #25, #26.

Check failure on line 499 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

No space after hash on atx style heading

CHANGELOG.md:499:1 MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#17, #18, #19, #20, #21, #22, ..."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md018.md

---

Expand All @@ -488,8 +504,8 @@

**Status**: RFC (Request for Comments) stage — minimum viable repository goals.

### Added

Check failure on line 507 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Headings should be surrounded by blank lines

CHANGELOG.md:507 MD022/blanks-around-headings Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "### Added"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md
- `docs/COLLECTION_STANDARD.md` — minimum media collection standard (audio,

Check failure on line 508 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Lists should be surrounded by blank lines

CHANGELOG.md:508 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- `docs/COLLECTION_STANDARD.md..."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md032.md
video, image, text, 3D) with hard rules and validation checklist.
- `docs/HIGH_FIDELITY_GUIDE.md` — aspirational high-fidelity collection
guide and quality rubric.
Expand All @@ -514,7 +530,7 @@
GitHub Issue Forms with explicit warnings against attaching sensitive
material publicly.

### Changed

Check failure on line 533 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Lint docs (markdownlint + linkcheck)

Headings should be surrounded by blank lines

CHANGELOG.md:533 MD022/blanks-around-headings Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "### Changed"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md
- `schemas/pointer.schema.json` — added `artifact_type`,
`media_metadata`, `encryption`, `retention_days`,
`withdrawal_supported`, `consent_ref`, `review_status`, `provenance`;
Expand Down
241 changes: 241 additions & 0 deletions docs/LIFE_RUNTIME_STANDARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,244 @@ A runtime that does not enforce all five is **not** compatible with
- Interop conformance test suite (deferred to `life-runtime v0.2`).
- Cryptographic signature verification of `signature_ref` (waits on
`life-format v0.2`).

---

# Part B — v0.8 normative additions (Topic 4)

> Status: **normative** for any runtime claiming `life-runtime ≥ 0.8`
> conformance. v0.7 runtimes MAY ignore Part B. The eight-step load
> sequence in §2 remains correct: Part B does not contradict it,
> rather it imposes a richer, named, five-stage discipline on top of
> it and adds three new normative concepts (Provider Registry,
> sandboxing tiers, bootstrap) that were not specified in v0.7.
>
> Closes sub-issue [#105](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/105) of epic
> [#106](https://github.com/Digital-Life-Repository-Standard/DLRS/issues/106).
>
> Cross-references:
>
> - Architecture overview: [`docs/LIFE_ASSET_ARCHITECTURE.md`](LIFE_ASSET_ARCHITECTURE.md) §6
> - Binding spec: [`docs/LIFE_BINDING_SPEC.md`](LIFE_BINDING_SPEC.md)
> - Tier spec: [`docs/LIFE_TIER_SPEC.md`](LIFE_TIER_SPEC.md)
> - Lifecycle spec: [`docs/LIFE_LIFECYCLE_SPEC.md`](LIFE_LIFECYCLE_SPEC.md)
> - Genesis spec: [`docs/LIFE_GENESIS_SPEC.md`](LIFE_GENESIS_SPEC.md)

## B.1 Five-stage assembly pipeline

The v0.7 load sequence (§2) is grouped into five named stages.
Loaders MUST execute them in order; any failure aborts assembly,
emits an audit event, and surfaces a structured rejection reason.

| Stage | v0.7 §-mapping | New normative additions in v0.8 |
|---|---|---|
| **1. Verify** | §2.1 + §2.2 + §2.3 + §2.4 + §2.5 | Lifecycle-state gate (`active` / `superseded` / `frozen+memorial` / `withdrawn`); withdrawal endpoint pre-flight; audit-chain hash-link integrity. |
| **2. Resolve** | (new) | Read `binding/runtime_binding.json`; map each capability to a Provider via the Provider Registry (B.2); apply tier-aware fallback (B.3). |
| **3. Assemble** | §2.6 + §2.7 + §2.8 | Instantiate Providers in their declared sandboxing class (B.4); inject `hard_constraints` + `surface.ui_hints.disclosure_label`; emit `capability_bound` audit event per capability. |
| **4. Run** | §3 + §4 (existing) | All v0.7 obligations remain. v0.8 adds: `forbidden_uses` MUST be applied via the same key namespace as `binding.hard_constraints` (hybrid: ~30 core keys + `x-` extensions, fail-close on unknown — see binding spec §7). |
| **5. Guard** | §4.3 + §5 + §6 | Withdrawal watcher (≥ every 24 h); lifecycle watcher (`superseded` / `frozen` / `withdrawn` transitions); expiry watcher; audit emitter. |

### B.1.1 Stage gating (decision **D6=fail-close**)

If any stage fails, loaders MUST:

1. Emit an audit event of type `assembly_aborted` with a `stage`
field (`verify | resolve | assemble | run | guard`) and a
structured `reason`.
2. Tear down any partially constructed Provider state.
3. Surface a localised rejection reason to the user (no opaque
"Failed to load" messages).
4. NOT silently fall back to a degraded mount.

This generalises the existing §2 hard-fail rule across the new
named stages.

## B.2 Provider Registry

A **Provider** is the v0.8 unit of capability execution. Each
Provider implements one or more capabilities declared in
`binding.capabilities` (e.g. `voice_synthesis`, `memory_recall`).

Loaders MUST maintain a **Provider Registry** with at least the
following operations:

- `list_providers(capability) -> [ProviderRef]` — enumerate every
Provider known to the runtime that exposes the given capability,
in deterministic priority order.
- `resolve(capability, engine_compatibility[]) -> ProviderRef` —
walk `engine_compatibility[]` (issuer-declared, ordered) and
return the first Provider whose `(name, version)` satisfies the
entry's `version_range` and `strict` flag (see binding spec §5.2).
- `metadata(ProviderRef) -> ProviderMetadata` — return at least the
Provider's `(name, version, sandbox_class)` so Stage 3 (Assemble)
can pick the correct sandbox.

The registry's storage shape is implementation-defined. Conformant
implementations are encouraged to expose it via a config file
(`~/.config/dlrs/providers.json` or equivalent) plus a `lifectl
provider list` CLI for inspection.

### B.2.1 The `LifeCapabilityProvider` interface

Every Provider MUST satisfy the following abstract interface
(language-agnostic; the names below are normative; signatures are
illustrative):

```
LifeCapabilityProvider:
capability_name() -> string # e.g. "voice_synthesis"
provider_name() -> string # e.g. "xtts-v2"
provider_version() -> semver
sandbox_class() -> "built_in" | "user_installed" | "bundled_in_life"

# Lifecycle (called by Stage 3 Assemble)
initialize(asset_paths: [path], params: dict, hard_constraints: dict) -> void
teardown() -> void

# Hot path (called by Stage 4 Run; per turn / per call)
invoke(input: dict) -> dict
```

Loaders MUST call `initialize` exactly once per mount, after
sandbox setup; MUST call `teardown` exactly once on unmount or
withdrawal; and MUST treat any exception from `invoke` as a
recoverable per-turn error (logged, audited, no automatic
unmount).

## B.3 Tier-aware resolution

Loaders SHOULD use the package's `tier` block (defined by
[`docs/LIFE_TIER_SPEC.md`](LIFE_TIER_SPEC.md)) when more than one
Provider matches an `engine_compatibility[]` entry:

| Tier band (level) | RECOMMENDED Provider preference |
|---|---|
| I–IV (low) | Lighter / offline / lower-fidelity Providers; preserve playability over fidelity. |
| V–VIII (mid) | Whatever the issuer's first `engine_compatibility[]` entry resolves to; no special bias. |
| IX–XII (high) | Higher-fidelity Providers; for capabilities permitted by `hosted_api_preference` (see B.5), MAY prefer hosted Providers. |

This is a SHOULD, not a MUST: loaders are free to ignore the tier
band if their environment dictates a fixed Provider choice (e.g. an
embedded runtime with one TTS engine only).

Loaders MUST honour `capability_binding.tier_floor` (binding spec
§5.1) when present: if the package's `tier.level` is below the
floor, the loader SHOULD warn the user before binding; whether to
proceed is a user / policy decision, not a hard refusal.

## B.4 Sandboxing classes (decision **D1=C, graded sandbox**)

Every Provider declares a sandbox class via `sandbox_class()`. The
runtime MUST enforce the following minimum boundary per class:

| Class | Trust assumption | Minimum boundary |
|---|---|---|
| `built_in` | Ships with the runtime; signed by the runtime vendor. | Same OS process as the runtime; no extra sandbox required. |
| `user_installed` | Installed by the user via OS package manager or `lifectl`. The user accepts that this code runs on their machine. | Runtime MUST run the Provider in a separate OS process with IPC; a stricter sandbox (firejail / nsjail / seccomp / wasm) is RECOMMENDED but not required. |
| `bundled_in_life` | Vendored inside the `.life` zip itself; untrusted. | **REJECTED at v0.8** (decision **D2=B**, see B.4.1). Not loadable until v1.0+ when a whitelisted-issuer scheme exists (decision **D2=C**). |

Loaders MUST refuse to bind a capability whose chosen Provider has
`sandbox_class() == "bundled_in_life"` until the v1.0+ whitelist
scheme lands. The binding schema rejects
`engine_kind: bundled_in_life` statically (binding spec §5.2), but
runtimes MUST also enforce this at Stage 2 (Resolve) as defence in
depth.

### B.4.1 Why `bundled_in_life` is forbidden in v0.8 (D2=B)

Letting an arbitrary `.life` ship arbitrary code is equivalent to
running an unsigned binary downloaded from the internet. The v0.8
ecosystem has no trust anchor that could authorise a third-party
`.life` to execute its own code; until issuer-whitelisting and
revocation are spec'd (target: v1.0+), `bundled_in_life` Providers
are unconditionally refused. This is intentional and is **not** a
schema bug.

## B.5 Hosted-API AND-gate (decision **D5=mixed**)

A hosted-API call MAY fire **only if** both halves of the AND-gate
say "allow":

```
ALLOW HOSTED CALL ⇔ binding.hosted_api_preference.allowed == true
AND policy/hosted_api.json permits this provider/capability
```

Either rejecting is sufficient. The default for a missing
`hosted_api_preference` block is `allowed: false` (binding spec §9).

The package's tier MAY influence the default user-side preference
in a runtime's UI (e.g. "this is a Tier IX package — would you like
to use hosted higher-fidelity providers? Y/N"), but the underlying
AND-gate is unchanged: the user retains the final veto. There is no
"recommend offline" or "recommend hosted" baked into the spec —
both modes are first-class (decision **D3=mixed**).

## B.6 Bootstrap (decision **D5=C, OS package manager**)

Users acquire a runtime via the host OS's package manager:

```
brew install dlrs-runtime # macOS
apt install dlrs-runtime # Debian / Ubuntu
winget install dlrs-runtime # Windows
```

The `.life` archive MUST NOT carry a self-extracting bootstrap
stub. Loaders MUST NOT auto-fetch the runtime from a `.life`. This
preserves the trust boundary: the runtime is something the user
explicitly installed, not something a `.life` can install on their
behalf.

When the OS does not have an associated `.life` handler, the OS
fallback (e.g. "find application to open this file") is permitted
to direct the user to `https://dlrs.standard/install` or an
equivalent canonical install page. The `.life` itself is inert
until a runtime is installed.

## B.7 Audit additions (v0.8)

In addition to the v0.7 audit-event vocabulary, conformant runtimes
MUST emit:

| Event type | When | Required fields |
|---|---|---|
| `capability_bound` | Once per capability after Stage 3 Assemble succeeds. | `capability`, `provider_name`, `provider_version`, `sandbox_class`. |
| `assembly_aborted` | Stage failure (B.1.1). | `stage`, `reason`. |
| `withdrawal_check` | Each withdrawal-watcher poll (Stage 5 Guard). | `endpoint`, `result`. |
| `lifecycle_transition_observed` | Stage 5 Guard observes a `lifecycle_state` transition (`active` → `superseded` / `frozen` / `withdrawn`). | `from_state`, `to_state`, `package_id`. |

Existing v0.7 events (`session_started`, `turn_emitted`, etc.) are
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Part B references non-existent v0.7 event name turn_emitted instead of session_turn

Line 641 in the new Part B text says "Existing v0.7 events (session_started, turn_emitted, etc.) are unchanged." However, the v0.7 audit event table at docs/LIFE_RUNTIME_STANDARD.md:288 defines the event as session_turn, not turn_emitted. There is no turn_emitted event anywhere in the repository. Implementers reading Part B who search for turn_emitted would find no definition, and could mistakenly create a new event type with that name instead of reusing the existing session_turn.

Suggested change
Existing v0.7 events (`session_started`, `turn_emitted`, etc.) are
Existing v0.7 events (`session_started`, `session_turn`, etc.) are
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

unchanged.

## B.8 What this update does NOT add

- **Provider sandbox implementation** — runtimes pick their own
sandbox technology (firejail, nsjail, wasm, etc.) per platform.
Spec only mandates the boundary.
- **Provider distribution registry** — Providers are distributed
via OS package managers / `lifectl` repositories; the spec does
not standardise the registry format itself yet (deferred to
`life-runtime 0.2`).
- **Runtime cryptographic identity** — Providers do not yet ship
signatures; trust is anchored on the OS package manager. A
signed-Provider scheme is deferred to v1.0+ alongside the
`bundled_in_life` whitelisting work.
- **Failover across runtimes** — if a Provider crashes, the runtime
MUST treat it as a per-turn error (B.2.1); migrating an active
mount to a different runtime instance is out of scope.

## B.9 Decisions encoded

| # | Decision | Realised in |
|---|---|---|
| **D1=C** | Graded sandbox (built-in / user-installed / `.life`-bundled) | B.4 |
| **D2=B** | v0.8: no bundled Providers | B.4 + B.4.1 |
| **D2=C** | v1.0+: whitelisted-issuer Providers (deferred) | B.8 |
| **D3=mixed** | Both offline and hosted are first-class | B.5 |
| **D4=C** | Three-field surface shape (`supported`, `preferred`, `minimum_required`) | binding spec §8 |
| **D5=C** | OS package manager bootstrap | B.6 |
| **D6=fail-close** | Stage failure aborts assembly, no degraded mount | B.1.1 |

[#105]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/105
[#106]: https://github.com/Digital-Life-Repository-Standard/DLRS/issues/106
2 changes: 1 addition & 1 deletion registry/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ <h1>DLRS public registry</h1>
</tbody>
</table>
<footer>
<p>1 record(s). Generated 2026-04-26T16:08:41Z. AI-generated outputs from records below MUST carry the disclosure declared in their <code>public_disclosure</code>.</p>
<p>1 record(s). Generated 2026-04-26T16:19:35Z. AI-generated outputs from records below MUST carry the disclosure declared in their <code>public_disclosure</code>.</p>
</footer>
</body>
</html>
Loading