commit 9677468e591b741583323a4065f0f35a62f29f32
parent 4a37ef1feefda4f0e6d95145dbf65dfadb8b15f2
Author: triesap <tyson@radroots.org>
Date: Mon, 3 Aug 2026 20:52:18 +0000
docs: remove ad-hoc documentation
Diffstat:
13 files changed, 0 insertions(+), 1318 deletions(-)
diff --git a/docs/adr/0001-rust-core-and-uniffi.md b/docs/adr/0001-rust-core-and-uniffi.md
@@ -1,18 +0,0 @@
-# ADR 0001: Rust core and UniFFI
-
-## Status
-
-Accepted and implemented.
-
-## Decision
-
-Implement the canonical Radroots Studio account and application runtime in a
-Rust workspace under `core/**`. Expose immutable snapshots, explicit commands,
-safe errors, and closeable observer subscriptions to Kotlin through UniFFI.
-Compose Desktop remains a thin native JVM 21 lifecycle and presentation shell.
-
-## Consequences
-
-Account behavior is reusable by future shells and cannot diverge in Kotlin.
-The build must generate Kotlin bindings, stage a platform native library, test
-callback and object lifetime behavior, and package that library with the app.
diff --git a/docs/adr/0002-nostr-library-selection.md b/docs/adr/0002-nostr-library-selection.md
@@ -1,24 +0,0 @@
-# ADR 0002: Nostr library selection
-
-## Status
-
-Accepted and implemented.
-
-## Decision
-
-Use `nostr-sdk` `0.44.1` and its compatible `nostr` `0.44.1` source for key
-generation, NIP-19, event/signature verification, and relay client behavior.
-The registry release of `nostr` `0.44.1` is yanked, so Cargo pins the upstream
-workspace commit `5bba5163eb77107f82c4a8262cf29d7f33a73219`. Keep SDK secret types inside a
-narrow adapter guarded by Radroots secret wrappers.
-
-Do not use Notedeck source or nostrdb. Both reviewed references are GPL and the
-MVP already requires migration-managed SQLite. Do not adopt the current
-`0.45.0-alpha` SDK line without a superseding compatibility ADR.
-
-## Consequences
-
-Local relay tests use the matching `nostr-relay-builder` `0.44.1` fixture and
-never require public network access. The direct dependency and license ledger
-is maintained in `docs/architecture/reference-research.md`; lockfile or version
-catalog changes require renewed compatibility and license review.
diff --git a/docs/adr/0003-account-metadata-and-secret-storage.md b/docs/adr/0003-account-metadata-and-secret-storage.md
@@ -1,27 +0,0 @@
-# ADR 0003: Account metadata and secret storage
-
-## Status
-
-Accepted and implemented.
-
-## Decision
-
-Store migration-managed, non-secret account metadata and profile cache in
-bundled SQLite at an injectable OS application-data location. Store local
-secret keys only in the OS credential store through Rust `SecretStore`, using
-service `org.radroots.studio.nostr` and canonical public-key hex as the account
-key.
-
-Use a non-secret operation journal because SQLite and the credential store
-cannot share an atomic transaction. Publish snapshots only after durable
-success. There is no ordinary-file secret fallback.
-
-Use `refinery 0.9.2` with bundled `rusqlite 0.39.0`. Refinery's supported
-Rusqlite range ends at 0.39, so this compatibility pin replaces the initially
-reviewed 0.40.1 candidate.
-
-## Consequences
-
-Startup recovery is mandatory. Add/import and removal need failure injection at
-every cross-resource boundary. Generated nsec is a one-time receipt exception;
-imported key text has a documented JVM String limitation.
diff --git a/docs/architecture/nostr-accounts.md b/docs/architecture/nostr-accounts.md
@@ -1,75 +0,0 @@
-# Nostr accounts architecture
-
-An import that resolves to an account with an existing credential returns
-`AccountAlreadyExists` and does not overwrite either resource. The sole repair
-path is an existing account explicitly marked `CredentialMissing` with no
-credential present; matching import restores that credential, changes the
-public availability state to `Available`, and retains the original metadata.
-
-## Status
-
-Implemented MVP architecture.
-
-## Ownership
-
-Rust `AppCore` owns the saved-account registry, selected account, active
-signer/session, persistence, recovery, relay/profile work, immutable snapshots,
-and safe errors. Kotlin is a thin lifecycle and presentation shell over UniFFI.
-`PersistentAppCore` composes the application ports with one SQLite `Database`;
-the FFI `StudioAppCore` adds the OS credential adapter, clock, SDK relay client,
-and observer ownership. `RadrootsApplication` creates one native core and one
-`StudioAppStore` for the application composition and closes both on disposal.
-
-## Identity and state
-
-Account identity is canonical lowercase Nostr public-key hex. Npub is a display
-encoding, and an optional local label is not identity. Saved, selected, and
-active are separate states. Startup restores saved accounts and selection but
-starts signed out.
-
-Npub and nsec domain values enforce their expected NIP-19 shape. The Nostr
-adapter performs checksum and key conversion validation. Nsec remains redacted
-and is exposed only in the import adapter or one-time generated-key receipt.
-
-Activation prepares a candidate signer/session before atomically replacing the
-working session. Sign out retains the saved account and credential. Confirmed
-removal deletes account-private state and credential, then chooses a
-deterministic selected fallback without activating it.
-
-Generate and import durably create and select a signed-out account. Import
-accepts nsec or canonical secret-key hex and rejects duplicates without
-overwriting credentials. A saved account explicitly marked
-`CredentialMissing` is the only repair exception. Removal requires a
-revision-bound, single-use confirmation token so stale UI confirmation cannot
-delete a changed target.
-
-Public state revisions increase only when public state changes. Observers get
-the current snapshot at subscription and later revisions after locks are
-released. A relay completion is bound to its initiating active public key; a
-sign-out or replacement makes the completion stale and prevents publication.
-
-## Data partitioning
-
-SQLite stores public account metadata, selected public key, verified kind-zero
-profile cache, account-owned typed preferences, migration state, and the
-non-secret recovery journal. Public Nostr events are keyed by event ID and
-author. Account-private values are keyed by owner public key and cascade on
-removal. Secret keys are never stored in SQLite.
-
-The production database is exactly
-`ProjectDirs::from("org", "radroots", "studio").data_dir()/studio.sqlite3`.
-The `directories` crate maps that base to the platform application-data area;
-tests inject temporary or in-memory databases instead.
-
-## Relay and profile flow
-
-Relays are global WebSocket endpoints from `RADROOTS_NOSTR_RELAYS`. Activation
-loads cached profile state. Refresh then queries kind-zero events using the
-production SDK adapter, verifies event ID, signature, author, kind, bounds, and
-metadata, and selects newest timestamp with lowest event ID as the tie-breaker.
-Offline, timeout, or invalid data retains cache and produces a nonfatal state.
-
-Remote relays require `wss://`. Plain `ws://` is restricted to localhost,
-`127.0.0.0/8`, and `::1`. Development mode falls back to
-`ws://localhost:8080`; packaged mode fails safely if no valid relay is
-configured. Tests use only ephemeral loopback relays or controlled fakes.
diff --git a/docs/architecture/radroots-crates-release-v1-reconciliation.md b/docs/architecture/radroots-crates-release-v1-reconciliation.md
@@ -1,63 +0,0 @@
-# Radroots crates release v1 reconciliation
-
-## Decision
-
-The Studio cutover described by Radroots crates release v1 Steps 274–278 is an
-evidence-based obsolete-source deviation for the current `studio_app` capsule.
-It must not be implemented by restoring the retired desktop runtime or by
-adding an unused `radroots_sdk` dependency.
-
-The crates-release review inspected Studio commit
-`8d849a204b0b67865603f5c877ad7409fc922d30`. That revision contained the GPUI
-workspace, `crates/runtime/src/sdk.rs`, `studio.sqlite`, direct Radroots lower
-crate dependencies, and sibling paths into `../lib` and `../sdk`. Commit
-`ae0179669c59261fe273a58d781763f69ad2b198` deleted that runtime. The current
-head descends from the reviewed revision and implements the later approved
-Rust/UniFFI/Compose Nostr-account architecture completed by the capsule's
-63-checkpoint runtime sequence.
-
-The current product does not implement the farms, listings, trades, generic
-storage, or generic synchronization semantics owned by `radroots_sdk`. Its
-canonical Rust `AppCore` owns only Studio account/session state and bounded
-kind-zero profile refresh. Adding the SDK without consuming those semantics
-would create a false dependency and two lifecycle owners.
-
-## Step reconciliation
-
-- Step 274 is satisfied by removal of all production sibling paths to the
- Radroots crate repositories. Workspace `path` dependencies are capsule-local
- composition between Studio-owned crates. There are no direct dependencies on
- a lower `radroots_*` package. Direct `nostr` and `nostr-sdk` dependencies are
- deliberate upstream protocol adapters for the current product contract.
-- Step 275's reviewed supervisor no longer exists. The replacement retains one
- host-owned `AppCore`, a supervised Tokio runtime, explicit async UniFFI
- commands, revision-bound results, closeable observers, and explicit shutdown.
-- Step 276 is satisfied by the Studio-owned migration-managed SQLite database
- under `core/crates/storage`. SDK backup or status code cannot own, mutate, or
- report this database because the current graph contains no SDK edge.
-- Step 277's generic signing, transport, and sync effects were deleted with the
- reviewed runtime. Current key handling stays behind `SecretStore`; profile
- fetch is bounded, local-relay tested, account/revision bound, and presentation
- state remains host-owned.
-- Step 278 is satisfied by the capsule's locked Rust and Gradle checks, native
- loader smoke, complete application tests, and current-host distribution
- package. A local Radroots registry canary is not applicable because the
- resolved graph contains no Radroots registry package.
-
-## Boundary guard
-
-The Studio manifest graph must continue to satisfy all of these conditions:
-
-1. no dependency path escapes the `studio_app` capsule;
-2. no `radroots_*` dependency is added merely to claim SDK migration;
-3. Studio account, preference, profile-cache, and presentation state remains
- host-owned;
-4. secrets remain in the operating-system credential adapter and never enter
- SQLite, DTOs, logs, or generated bindings;
-5. any future farms, listings, trades, or generic sync product surface must be
- introduced through the then-current `radroots_sdk` API and its real package
- artifacts, not by restoring the deleted runtime.
-
-This reconciliation preserves the crates-release architecture requirement:
-consumers use the correct layer for the semantics they actually consume, and a
-new SDK edge is required when Studio first consumes SDK-owned semantics.
diff --git a/docs/architecture/reference-research.md b/docs/architecture/reference-research.md
@@ -1,144 +0,0 @@
-# Nostr account runtime reference research
-
-## Status
-
-Baseline recorded on 2026-08-02 and reconciled against the implemented runtime
-on the same date.
-
-## Local baseline
-
-- Repository: standalone `oss/studio_app` capsule.
-- Branch: `master`.
-- Baseline commit: `9cd07ced24d62eca4cc89f09c82a3539c7fe9dcf`.
-- Runtime: Kotlin JVM 21 and Compose Desktop.
-- Current runtime: Rust-owned account state and command handling, SQLite public
- persistence, OS credential storage, rust-nostr relay access, and UniFFI DTOs.
-- Kotlin is a thin Compose shell with presentation-only input, backup, chooser,
- busy, and safe-problem state.
-- Baseline verification: `./gradlew --no-daemon :app:desktop:test` passed.
-
-## Reviewed references
-
-### Nostr protocol
-
-- Repository: `nostr-protocol/nips`.
-- Commit: `c53877571f96eb423661fc23c620d629d37b8f19`.
-- Paths: `01.md`, `19.md`.
-- Adopt: lowercase 32-byte public-key hex in protocol/storage, WebSocket relay
- transport, kind-0 metadata, signed-event verification, replaceable-event
- ordering, and npub/nsec as human-facing encodings.
-
-### Amethyst
-
-- Repository: `vitorpamplona/amethyst`.
-- Commit: `bf41e75b78b3891e5434baa4e5778a93988ba2ca`.
-- Reviewed paths: `README.md`, `docs/secure-key-storage-migration.md`,
- `docs/plans/archive/2026-04-23-feat-desktop-multi-account-support-plan.md`,
- `docs/plans/archive/2026-04-28-multi-account-testing-sheet.md`,
- `docs/plans/archive/2026-05-14-fix-account-security-hardening-plan.md`,
- JVM `SecureKeyStorage.kt`, `AccountManager.kt`,
- `DesktopAccountStorage.kt`, and their account/keyring tests.
-- Adopt: pubkey identity, multiple saved accounts, explicit corruption state,
- account-scoped resources, and transition tests for missing credentials,
- logout, and account replacement.
-- Reject: encrypted ordinary-file secret fallback, publishing in-memory cache
- before durable writes, mutating the replacement session before cleaning the
- captured old session, swallowed deletion failures, and silent conversion of
- a missing local credential into a read-only account.
-
-### Notedeck
-
-- Repository: `damus-io/notedeck`.
-- Commit: `b41ffeb57636f9de3147803c1313d99dda6cffa2`.
-- Reviewed paths: `README.md`, `LICENSE`,
- `crates/notedeck/src/account/accounts.rs`,
- `crates/notedeck/src/account/cache.rs`,
- `crates/notedeck/src/storage/account_storage.rs`,
- `crates/notedeck/src/storage/keyring_store.rs`, and
- `crates/notedeck/src/user_account.rs`.
-- License: GPL-3.0-or-later. Architectural comparison is clean-room only; no
- source is copied.
-- Adopt conceptually: pubkey-keyed account cache, selected-account persistence,
- in-memory credential fakes, and account-specific resources.
-- Reject: constructors that permit secrets on disk, file-first deletion without
- recovery, cache mutation before storage success, a synthetic fallback
- account, hash-map-order selection, and cloneable secret-bearing account types.
-
-### rust-nostr and nostr-sdk
-
-- Repository: `nostrdevkit/nostr`.
-- Commit reviewed: `7834dd624dcc8bdce9610988eb0b5e888b73c2eb`.
-- Reviewed paths: workspace manifests, `nostr-keyring/**`, key types,
- `nostr-sdk/src/local_relay/**`, and SDK client/profile APIs.
-- License: MIT.
-- Dependency baseline: `nostr-sdk` remains at `0.44.1`. Because crates.io
- yanked `nostr` `0.44.1`, the matching `nostr` workspace package is pinned to
- upstream commit `5bba5163eb77107f82c4a8262cf29d7f33a73219`; do not adopt the reviewed
- repository's `0.45.0-alpha` line without a later ADR change.
-- Adopt: maintained key/event/NIP implementations and SDK relay behavior.
-- Contain: upstream secret/key types may implement `Clone` or `Debug`; keep them
- behind a non-cloneable, redacted Radroots adapter boundary.
-
-### nostrdb
-
-- Repository: `damus-io/nostrdb`.
-- Commit: `f4591db9524bc4936af76af4750ec425e67700be`.
-- Reviewed paths: `README.md`, `LICENSE`, and build/storage entry points.
-- License: GPL-3.0-or-later.
-- Decision: do not use. The handoff requires migration-managed non-secret
- SQLite, and the additional database and license are unnecessary for the MVP.
-
-### UniFFI
-
-- Repository: `mozilla/uniffi-rs`.
-- Commit: `2ccd07e219161c51a5642b4d7be8f174a846462f`.
-- Reviewed paths: `README.md`, `LICENSE`, async internals/overview,
- `docs/manual/src/kotlin/**`, callback-interface documentation, Kotlin future
- and callback binding tests, and JNI thread-attachment runtime code.
-- Release baseline: `0.32.0`, MPL-2.0.
-- Adopt: generated Kotlin bindings, asynchronous operations, explicit object
- disposal, and callback handles.
-- Guard: invoke no observer while holding Rust locks; serialize revisions;
- verify JVM thread attachment, cancellation, deregistration, and close races.
-
-## Implemented dependency and license ledger
-
-The following direct dependencies are pinned by `core/Cargo.toml`,
-`core/Cargo.lock`, and `gradle/libs.versions.toml`. License identifiers were
-reconciled from the checked-out upstream Cargo manifests or resolved Maven POMs.
-
-| Dependency | Pin | License | Runtime role |
-| --- | --- | --- | --- |
-| `nostr` | git `5bba5163eb77107f82c4a8262cf29d7f33a73219` (`0.44.1`) | MIT | keys, NIP-19, events |
-| `nostr-sdk` | `0.44.1` | MIT | relay client |
-| `nostr-relay-builder` | `0.44.1` | MIT | test-only local relay |
-| `uniffi` | `0.32.0` | MPL-2.0 | Rust/Kotlin bindings |
-| `keyring` | `4.1.6` | MIT OR Apache-2.0 | OS credential adapter |
-| `rusqlite` | `0.39.0` | MIT | bundled SQLite adapter |
-| `refinery` | `0.9.2` | MIT | SQLite migrations |
-| `secrecy` | `0.10.3` | Apache-2.0 OR MIT | redacted secret values |
-| `zeroize` | `1.9.0` | Apache-2.0 OR MIT | secret-memory cleanup support |
-| `directories` | `6.0.0` | MIT OR Apache-2.0 | canonical data location |
-| `url` | `2.5.8` | MIT OR Apache-2.0 | relay URL parsing |
-| `tokio` | `1.47.1` | MIT | async runtime |
-| `tempfile` | `3.23.0` | MIT OR Apache-2.0 | test-only isolated storage |
-| Kotlin/JVM | `2.4.10` | Apache-2.0 | JVM language and tooling |
-| Compose Multiplatform | `1.11.1` | Apache-2.0 | desktop UI |
-| kotlinx.coroutines | `1.9.0` | Apache-2.0 | thin-store command dispatch |
-| JNA | `5.17.0` | LGPL-2.1-or-later OR Apache-2.0 | native library loading |
-
-The workspace itself is GPL-3.0-only. SQLite's bundled C implementation is
-public domain; `rusqlite` remains MIT. Exact Rust transitive resolution is
-committed in `core/Cargo.lock`; Gradle dependency verification uses the pinned
-version catalog and wrapper distribution. A dependency upgrade requires
-re-running the license and compatibility review, including the UniFFI loader
-and current-platform package smoke.
-
-## Final selection rationale
-
-rust-nostr provides maintained protocol primitives and relay behavior while
-allowing Radroots to keep domain, persistence, secret, and lifecycle contracts
-behind its own ports. Notedeck supplied useful architectural comparison, but
-adopting its lower-level account/storage stack would duplicate the selected
-SQLite and state-machine ownership and would complicate clean-room provenance.
-No Notedeck or nostrdb source was copied.
diff --git a/docs/architecture/rust-ffi-boundary.md b/docs/architecture/rust-ffi-boundary.md
@@ -1,73 +0,0 @@
-# Rust and UniFFI boundary
-
-## Status
-
-The proc-macro UniFFI namespace is `radroots_studio_ffi`. Kotlin bindings use
-the package `org.radroots.studio.ffi`; generated sources remain build output.
-
-## Contract
-
-One Rust `AppCore` instance owns canonical state. Kotlin receives immutable,
-revisioned DTO snapshots, invokes explicit commands, and subscribes through a
-closeable observer handle. Mutations are serialized, callbacks occur outside
-locks, and stale asynchronous results cannot replace newer state.
-
-Normal public DTOs contain no secret. The only exception is the direct,
-one-time generated-key receipt. Generated bindings belong under
-`build/generated` and are not committed.
-
-Potentially blocking storage and credential operations use Rust blocking tasks;
-relay operations use the supervised Tokio runtime. UniFFI exports them as
-suspending Kotlin calls. `StudioAppStore` accepts at most one command at a
-time, keeps Compose state read-only to consumers, rejects stale observer
-revisions, and closes its removal ticket, subscription, gateway, and native
-core during application disposal.
-
-The public snapshot DTO carries revisioned lifecycle, account, session, relay,
-profile, and safe-error values. It contains no credential field. A generated
-account's nsec is confined to the explicit one-time receipt added with the
-command boundary.
-
-The exported command surface is bootstrap, snapshot, generate, import, select,
-activate, sign out, profile refresh, removal request/confirmation, subscribe,
-unsubscribe, and shutdown. `ObserverSubscription.unsubscribe` is idempotent;
-`StudioAppCore.shutdown` deregisters remaining observers and signs out before
-the generated object handle is closed. Callback tests re-enter `snapshot`,
-observe asynchronous profile refresh, and prove no callback arrives after
-unsubscribe.
-
-## Native artifact
-
-The FFI crate builds as both an `rlib` for Rust tests and a `cdylib` for the JVM
-binding. Build the current-host development artifact from the capsule root:
-
-```sh
-cargo build --manifest-path core/Cargo.toml -p radroots-studio-ffi
-```
-
-On macOS this produces
-`core/target/debug/libradroots_studio_ffi.dylib`. Other desktop hosts use the
-platform-equivalent `radroots_studio_ffi` dynamic-library filename under the
-same Cargo profile directory.
-
-Gradle exposes the same operation as `:app:desktop:buildRustCore`. The task
-tracks Rust manifests, lockfile, sources, and migrations as inputs and the
-current-host debug dynamic library as its output. It does not redirect Cargo's
-target directory.
-
-`:app:desktop:generateUniFfiKotlin` runs the repository-pinned UniFFI 0.32
-generator against that dynamic library and writes Kotlin to
-`app/desktop/build/generated/uniffi/kotlin`. The main Kotlin source set reads
-that generated directory, and `compileKotlin` depends on generation. Generated
-bindings are ignored build output and are never committed.
-
-Development and tests point JNA at `core/target/debug`. Packaging stages the
-current-host library under JNA's platform resource prefix inside the desktop
-resources, allowing the packaged JVM runtime to extract and load it without a
-machine-specific absolute path. `NativeLoaderTest` crosses the generated ABI
-and verifies the native crate version without opening storage or credentials.
-
-Current platform resource prefixes are `darwin-aarch64`, `darwin-x86-64`,
-`linux-aarch64`, `linux-x86-64`, `win32-aarch64`, and `win32-x86-64`. The build
-stages only the current host artifact; each release platform must build and
-smoke its own package rather than reusing another platform's dynamic library.
diff --git a/docs/implementation/nostr-runtime-rcld.md b/docs/implementation/nostr-runtime-rcld.md
@@ -1,571 +0,0 @@
-# Nostr runtime multi-RCLD
-
-## Status
-
-- Program status: completed.
-- Active RCLD: none.
-- Active atomic checkpoint: none.
-- Repository: `oss/studio_app` standalone Git repository.
-- Baseline branch: `master`.
-- Baseline commit: `9cd07ced24d62eca4cc89f09c82a3539c7fe9dcf`.
-- Implementation commits require a separate execution directive.
-
-## Objective
-
-Replace the Kotlin-only generic account-server proof with a Nostr-only desktop
-application whose canonical account and application runtime is implemented in
-Rust under `core/**`. Compose Desktop remains a thin native JVM 21 shell over a
-UniFFI boundary. The UI proves the complete local Nostr accounts model with
-minimal primitives; it does not introduce the final product design system.
-
-## Authority and repository boundary
-
-The sole documentary authority for this program is the handoff package at:
-
-`../../docs/handoff/radroots_studio_app_v1_kotlin_basis_handoff/**`
-
-The relative path above is from the `oss/studio_app` capsule root. This plan is
-a derived execution ledger and is subordinate to that package. Other monorepo
-documents and the legacy `/studio_app` tree are not implementation authority.
-
-All source changes belong inside this standalone `oss/studio_app` repository.
-Do not stage or commit its gitlink from the parent repository as part of these
-RCLDs. Do not modify the parent repository or the legacy `/studio_app` tree.
-
-## Execution contract
-
-1. Execute the RCLDs in numeric order.
-2. Keep only one RCLD and one atomic checkpoint active at a time.
-3. Preserve the 63 checkpoint order defined below. Do not merge, skip, or
- reorder checkpoints merely to reduce commit count.
-4. Before each checkpoint, inspect its authority and the nested repository
- status. Reconcile the remaining ledger after each green checkpoint.
-5. Implement the smallest complete change for the active checkpoint.
-6. Run its narrow verify lane plus every relevant inherited regression lane.
-7. Repair or split a red checkpoint. Never commit a red checkpoint.
-8. Commit only when an execution directive explicitly authorizes commits.
-9. Never push, publish, deploy, or update the parent gitlink without separate
- authorization.
-10. Record any necessary deviation in this document before implementation. A
- deviation may clarify an unsafe instruction but may not broaden product
- scope.
-
-## Approved constraints and normalized decisions
-
-### Build and repository policy
-
-- Do not use extbuild in this OSS capsule.
-- Do not add `.github/**`, `scripts/**`, empty directories, or placeholder
- future modules.
-- Keep the Makefile as the human-facing lifecycle surface. It delegates to
- repository-owned Cargo and Gradle tasks.
-- Keep generated UniFFI Kotlin, staged native libraries, Cargo output, Gradle
- output, and packaged artifacts under ignored build directories.
-- Preserve package `org.radroots.studio`, Gradle module `:app:desktop`, JVM 21,
- the pinned Kotlin and Compose versions, the application icon, native package
- identity, transparent macOS title bar, 1284 by 795 initial window size, 1080
- by 720 minimum size, and fail-closed Kotlin test discovery.
-- Normalize application and package version metadata around `0.1.0-alpha`; use
- an installer-compatible numeric form only where a native packaging tool
- rejects prerelease text, and document that mapping.
-
-### Runtime ownership
-
-- Rust `AppCore` is the sole canonical owner of accounts, selection, active
- session, persistence, recovery, relay/profile work, snapshot revisions, and
- safe errors.
-- Kotlin owns native-window lifecycle, generated-binding lifecycle,
- presentation-only state, user input, command dispatch, and immutable DTO
- rendering.
-- Kotlin must not reproduce the Rust reducer, session state machine, storage
- policy, or relay policy.
-- Mutating AppCore commands are serialized. Public snapshots are immutable and
- revisioned monotonically.
-- Observers are never called while an internal lock is held. Notifications are
- serialized in revision order and stop after deregistration.
-
-### Account and session semantics
-
-- Canonical account identity is a lowercase 64-character Nostr public-key hex
- value. `npub` is a human-facing encoding, not a storage key.
-- A local label is optional and is distinct from Nostr `name` and
- `display_name` profile fields.
-- Generate and import persist and select an account but do not activate it.
-- Startup restores saved accounts and persisted selection but starts signed
- out. It does not access a credential or relay until explicit activation.
-- Selection and active session are independent. Selecting a candidate does not
- destroy the current active session.
-- Activation prepares and validates the candidate credential, signer, cached
- profile, and relay session before atomically replacing a working session.
- Failure preserves the previous session.
-- Sign out cancels account tasks and drops signer/session state while retaining
- account metadata, selection, and credential.
-- Removal uses a single-use confirmation token bound to the target pubkey and
- snapshot revision. Active removal signs out first and never auto-activates a
- fallback.
-- Removing a selected account chooses the next persisted account, then the
- preceding account. Empty registries have no selection.
-- An existing account and credential returns `AccountAlreadyExists` without
- overwrite. Matching import into an explicit `CredentialMissing` account is
- the supported repair path.
-
-### Secret boundary
-
-- Production secret keys exist only in the OS credential store through a Rust
- `SecretStore`; there is no file, preference, encrypted-file, or plaintext
- fallback.
-- Use credential service `org.radroots.studio.nostr` and canonical pubkey hex
- as its account key.
-- Application secret wrappers are non-`Clone`, non-`Debug`, non-`Display`, and
- non-serializable. Cloneable or debug-capable upstream SDK secret types remain
- inside the narrow signer adapter and never cross domain or FFI boundaries.
-- Secrets are forbidden in SQLite, profile cache, operation journals,
- snapshots, normal public DTOs, logs, errors, filenames, preferences,
- analytics, fixtures, and golden files.
-- `GenerateAccountReceipt.generated_nsec` is the sole transient public DTO
- exception. It never enters `AppSnapshot` or persistence.
-- Kotlin holds generated nsec only in non-saveable presentation state. It is
- cleared on acknowledgement, replacement, or application disposal.
-- Copying generated nsec is explicit. Clear the clipboard after 60 seconds only
- when it still contains the copied value.
-- Imported key text is masked, submitted once, and cleared as soon as the
- command is accepted, before coroutine or native execution. The JVM
- limitation is documented and tested as far as the platform permits.
-
-### Persistence and recovery
-
-- Use migration-managed bundled SQLite at an injectable OS application-data
- location.
-- Persist accounts, selected account, bounded profile cache, typed non-secret
- account preferences, and a non-secret cross-resource operation journal.
-- Publish success only after durable state commits. Never update the public
- snapshot first and merely log a persistence failure.
-- Add/import recovery covers credential write, metadata commit, compensation,
- compensation failure, and restart.
-- Removal recovery records intent, credential deletion, metadata deletion, and
- finalization as distinct phases. A deleted credential is never represented
- as restorable.
-- Missing credentials produce an explicit `CredentialMissing` repair state,
- not an implicit read-only signer.
-- Corrupt or unsupported databases fail safely and are not destructively
- recreated without an explicit future recovery feature.
-- Do not expose arbitrary `set_account_scoped_value` or
- `get_account_scoped_value` methods through shipping UniFFI. Prove account
- partitioning through a typed Rust repository interface and integration tests
- until a real typed preference is required.
-
-### Relay and profile behavior
-
-- Read the ordered relay list from comma-separated
- `RADROOTS_NOSTR_RELAYS`. Trim and deduplicate while preserving order.
-- Accept `wss://`. Accept `ws://` only for `localhost`, `127.0.0.0/8`, and
- `::1`. Reject user information, fragments, and non-WebSocket schemes.
-- Use `ws://localhost:8080` only as a development/test fallback. A packaged
- runtime without valid configuration reports a safe configuration state.
-- Tests use local ephemeral relays or controlled fakes and never public
- internet relays.
-- Verify kind-0 event ID, signature, author, and kind before parsing. Select the
- newest `created_at`; break equal timestamps with the lexicographically lowest
- event ID.
-- Bound relay messages, raw event content, parsed profile fields, and stored
- profile data.
-- Emit cached profile state before asynchronous refresh. Timeout, offline, or
- invalid relay data preserves the cache and produces a nonfatal state.
-- Display `nip05` and `picture` as bounded metadata. NIP-05 verification and
- remote-image fetching are out of scope.
-
-### UniFFI and Kotlin lifecycle
-
-- Use UniFFI async operations for work that could block the Compose thread.
-- Supervise Rust relay tasks and cancel them on replacement, sign out, and
- AppCore close.
-- Bind asynchronous results to their initiating account and revision so stale
- results cannot overwrite newer state.
-- Ensure callback delivery is safe for JVM thread attachment and callback
- re-entry. Close observer handles and AppCore explicitly from Kotlin.
-- Generated bindings remain under `build/generated` and are never hand-edited.
-- Keep one AppCore instance at the application root. The Kotlin store subscribes
- once, exposes read-only Compose state, forwards commands, and closes cleanly.
-
-### Minimal product UI
-
-- Provide an inactive accounts screen with generate, masked import, saved
- accounts, selection, activation, and confirmed removal.
-- Provide a one-time generated-key backup panel with npub, nsec, warning, copy,
- and acknowledgement.
-- Provide a minimal active-account screen with pubkey, npub, cached profile,
- relay/profile state, configured relays, refresh, switch-account, and sign-out
- controls.
-- Use basic Compose primitives, existing limited styling, stable test tags, and
- accessibility descriptions. Do not add a design system.
-- Remove UUID account identity, obsolete remote-endpoint fields, fake session
- flags, the canonical Kotlin reducer/store, and all generic remote-onboarding
- language from active source and tests.
-
-### Dependency baseline
-
-Pin exact compatible versions in the dependency checkpoint and record their
-licenses and primary-source provenance:
-
-- stable `nostr` and `nostr-sdk` `0.44.1`;
-- UniFFI `0.32.0`;
-- `keyring` `4.1.6`, behind the Radroots `SecretStore`;
-- `rusqlite` `0.39.0` with bundled SQLite, constrained by Refinery `0.9.2`;
-- `refinery` `0.9.2`;
-- `secrecy` `0.10.3`;
-- `zeroize` `1.9.0`;
-- `directories` `6.0.0`.
-
-Dependency-source deviation: crates.io has yanked `nostr` 0.44.1 and refuses a
-new exact registry resolution. The `nostr` crate is therefore pinned to the
-upstream rust-nostr commit `5bba5163eb77107f82c4a8262cf29d7f33a73219`,
-whose crate manifest declares version 0.44.1. This preserves the approved
-version and uses the upstream source rather than substituting a newer release.
-
-Notedeck and nostrdb are GPL clean-room references only. Do not copy their
-code. Reject optional disk-secret storage, nondeterministic account fallback,
-cache-before-persist publication, and delete-without-recovery patterns.
-Reject Amethyst's encrypted-file fallback, mutation-before-cleanup transition,
-swallowed deletion failures, and memory-cache-before-disk publication.
-
-## Global definition of green
-
-Every checkpoint must satisfy all applicable items below before it can be
-committed:
-
-- formatting is clean;
-- warnings are denied in first-party Rust code;
-- the narrow Rust or Kotlin unit tests pass;
-- inherited tests for completed behavior remain green;
-- no public-internet relay is contacted by tests;
-- no generated or build output is staged;
-- no secret or generic server-account concept has crossed a forbidden
- boundary;
-- the diff contains only the active checkpoint;
-- the nested repository is the only repository being changed;
-- this ledger is updated if checkpoint status or remaining scope changed.
-
-There is no numeric coverage threshold. The required standard is strong,
-best-effort behavioral coverage across Rust unit, storage, recovery, FFI,
-Kotlin store, Compose UI, local relay, restart, native-loader, and end-to-end
-lanes.
-
-## RCLD sequence
-
-### RCLD-01: Authority and dependency baseline
-
-Status: completed.
-
-Scope: checkpoint 1. Record live repository state, authoritative handoff files,
-reference source SHAs and paths, adopted and rejected ideas, license boundaries,
-dependency versions, native platform assumptions, and the approved normalized
-decisions in capsule-local research and ADR material.
-
-Definition of green: the recorded facts match the live checkout and primary
-sources; GPL references are clean-room only; no runtime source has changed.
-
-Verify lane: Git boundary/status inspection, primary-source version and license
-checks, documentation link/path validation, and diff review.
-
-### RCLD-02: Rust workspace and domain
-
-Status: completed.
-
-Scope: checkpoints 2 through 11. Establish the Rust workspace and implement
-safe errors, public keys, secret input/redaction, NIP-19 types, relay URLs,
-account metadata, kind-0 profile rules, and immutable application snapshots.
-
-Definition of green: all domain values are validated types; secret wrappers are
-redacted and non-cloneable; snapshots contain no secret fields; domain tests
-cover valid, invalid, canonicalization, ordering, and invariant cases.
-
-Verify lane: Cargo formatting, clippy with warnings denied, workspace check,
-and focused domain tests.
-
-### RCLD-03: Application state machine
-
-Status: completed.
-
-Scope: checkpoints 12 through 14. Define repository, secret, clock, and Nostr
-ports; implement in-memory AppCore bootstrap, command serialization, monotonic
-revisions, transition helpers, and observer registration/deregistration.
-
-Definition of green: selected and active state are separate; transitions are
-deterministic; callbacks occur outside locks; observer lifecycle and command
-traces are tested.
-
-Verify lane: application crate formatting, clippy, check, unit tests, callback
-re-entry tests, and workspace regression tests.
-
-### RCLD-04: SQLite persistence
-
-Status: completed.
-
-Scope: checkpoints 15 through 20. Add bundled SQLite and migrations, account and
-selection persistence, profile cache, typed account-scoped partitioning,
-operation journal storage, and persisted AppCore bootstrap.
-
-Definition of green: fresh, migrated, restarted, isolated, corrupt, and
-idempotent database cases are covered; no secret appears in schema or data;
-snapshots publish only after successful commits.
-
-Verify lane: storage formatting, clippy, migration tests, restart tests,
-failure-injection tests, database-byte secret guards, and workspace tests.
-
-### RCLD-05: Credential boundary
-
-Status: completed.
-
-Scope: checkpoints 21 through 24. Add SecretStore, in-memory and
-failure-injection fakes, OS keyring adapter, safe platform errors, and global
-no-secret guards.
-
-Definition of green: production has no fallback; adapter service/key naming is
-stable; unavailable, missing, duplicate, read, write, and delete outcomes map
-to safe errors; known secrets are absent from snapshots, persistence, errors,
-and captured logs.
-
-Verify lane: secret-store unit and contract tests, platform adapter smoke where
-available, redaction tests, Cargo checks, and inherited storage tests.
-
-### RCLD-06: Account generation and import
-
-Status: completed.
-
-Scope: checkpoints 25 through 30. Pin and adapt the selected Nostr library;
-implement generation, nsec and secret-hex import, duplicate and repair
-semantics, cross-resource rollback, journaling, and persisted commands.
-
-Definition of green: derived pubkeys and NIP-19 values match known vectors;
-generated nsec appears only in its receipt; duplicate imports do not overwrite;
-every keyring/database failure boundary is tested; partial success is never
-published.
-
-Verify lane: Nostr vector tests, application command tests, transaction and
-restart failure-injection matrix, no-secret guards, and workspace regression.
-
-### RCLD-07: Account lifecycle and recovery
-
-Status: completed.
-
-Scope: checkpoints 31 through 35. Implement selection, signed-out startup,
-safe replacement activation, sign out, revision-bound removal confirmation,
-deterministic fallback, and removal journal recovery.
-
-Definition of green: failed replacement preserves the active session; sign out
-retains saved state; stale confirmation is rejected; removal is deterministic;
-irreversible deletion phases are represented honestly and recover on restart.
-
-Verify lane: state-machine tables, concurrent/stale command tests, activation
-failure tests, removal failure-injection matrix, restart recovery, cancellation,
-and workspace tests.
-
-### RCLD-08: Relay and profile runtime
-
-Status: completed.
-
-Scope: checkpoints 36 through 40. Add environment relay configuration, exact
-WebSocket policy, verified kind-0 parsing, deterministic local relay fixture,
-cache-first asynchronous refresh, and manual refresh.
-
-Definition of green: relay parsing follows the approved policy; invalid events
-are rejected; equal-time tie-breaking is deterministic; cached state arrives
-before refresh; stale, offline, timeout, cancellation, and repeated refresh
-outcomes are safe; tests use no public relay.
-
-Verify lane: relay parser tests, NIP event vectors, local ephemeral relay tests,
-async stale-result/cancellation tests, profile cache tests, and workspace tests.
-
-### RCLD-09: UniFFI and native build integration
-
-Status: completed.
-
-Scope: checkpoints 41 through 47. Add UniFFI definitions and safe DTO mapping,
-expose supported AppCore commands, implement observer handles, build the native
-library, generate Kotlin bindings, and stage native artifacts for development,
-tests, and Compose packaging.
-
-Definition of green: all shipping commands are callable without the generic
-namespace API; generated DTOs contain no ordinary secret field; async calls do
-not block Compose; callbacks are ordered and close safely; generated source is
-not tracked; development and packaged native loading work on the current host.
-
-Verify lane: Cargo FFI tests, bindgen freshness/generation, ABI and loader smoke,
-Gradle compile/test, observer cancellation/re-entry tests, package staging
-inspection, and secret-field source guards.
-
-### RCLD-10: Thin Kotlin shell and minimal UI
-
-Status: completed.
-
-Scope: checkpoints 48 through 57. Implement the thin StudioAppStore, create and
-close one AppCore at the application root, map public DTOs, implement inactive,
-backup, saved-account, active-home, switch, sign-out, refresh, and removal UI,
-then remove the canonical Kotlin proof and generic server-account remnants.
-
-Definition of green: the complete MVP is operable through minimal Compose
-controls; generated secrets remain ephemeral; all important controls have
-stable tags and accessibility descriptions; no UUID, remote endpoint, fake login,
-or duplicated Kotlin state machine remains.
-
-Verify lane: Kotlin unit tests, Compose UI tests for success and failure flows,
-clipboard fake/timer tests, AppCore lifecycle tests, full Gradle check, source
-guards, and native-loader smoke.
-
-### RCLD-11: End-to-end integration hardening
-
-Status: completed.
-
-Scope: checkpoints 58 and 59. Add full local-relay end-to-end coverage plus
-restart, account isolation, FFI callback, cancellation, deregistration, stale
-completion, and no-secret integration tests.
-
-Definition of green: generate/import, activate, cached profile, relay refresh,
-sign out, switch, remove, restart, repair, and callback lifecycle are proven
-without public network access; account A cannot observe account B's private
-namespace; secret guards pass across persisted and serialized artifacts.
-
-Verify lane: full Cargo workspace test, focused end-to-end suites, Gradle test,
-Compose tests, native-loader smoke, and repeated runs for async determinism.
-
-### RCLD-12: Documentation and acceptance reconciliation
-
-Status: completed.
-
-Scope: checkpoints 60 through 63. Complete dependency/license, architecture,
-security, testing, local-relay, FFI lifecycle, recovery, and platform validation
-documentation; finish the Makefile lifecycle; perform the final source audit
-and reconcile every acceptance criterion.
-
-Definition of green: required capsule documentation matches implemented paths
-and behavior; Makefile exposes doctor, format, lint, test, check, build, dev,
-run, package, and clean without extbuild or scripts; current-host package smoke
-passes; portable loader concerns for macOS, Linux, and Windows are documented;
-the final ledger records every command and residual platform risk honestly.
-
-Verify lane: all Makefile lifecycle targets applicable without destructive
-cleanup, Cargo workspace format/clippy/check/test, Gradle check/test/build,
-UniFFI generation, loader smoke, local-relay end-to-end, current-OS packaging,
-forbidden-path and forbidden-term guards, Git status, and full diff audit.
-
-## Atomic checkpoint ledger
-
-All checkpoints are complete. Their order and titles are inherited from the
-handoff commit sequence.
-
-### RCLD-01
-
-- [x] 01. Audit live repository and reference sources.
-
-### RCLD-02
-
-- [x] 02. Establish root Rust workspace skeleton.
-- [x] 03. Add Rust formatting, lint, and governed check hooks. Use Cargo,
- Gradle, and Makefile; do not add CI workflows or scripts.
-- [x] 04. Define domain module layout and safe error shell.
-- [x] 05. Implement Nostr public key value object.
-- [x] 06. Implement secret input boundary and redacted secret wrapper.
-- [x] 07. Add NIP-19 public/secret display contract types.
-- [x] 08. Implement relay URL parser and policy.
-- [x] 09. Add account public metadata value types.
-- [x] 10. Add profile metadata model and kind-0 selection rules.
-- [x] 11. Define immutable AppSnapshot and state enums.
-
-### RCLD-03
-
-- [x] 12. Add application ports for repositories, secrets, clock, and Nostr
- client.
-- [x] 13. Implement in-memory AppCore bootstrap and observer registry.
-- [x] 14. Add state transition helpers and command trace tests.
-
-### RCLD-04
-
-- [x] 15. Create SQLite storage crate and migration runner.
-- [x] 16. Implement account and selected-account persistence.
-- [x] 17. Implement profile cache persistence.
-- [x] 18. Implement typed account-scoped namespace persistence.
-- [x] 19. Add operation journal persistence.
-- [x] 20. Implement storage adapter wiring for AppCore bootstrap.
-
-### RCLD-05
-
-- [x] 21. Add SecretStore trait and in-memory fake.
-- [x] 22. Add failure-injection SecretStore fake.
-- [x] 23. Implement OS keyring secret adapter.
-- [x] 24. Add global no-secret snapshot and storage assertions.
-
-### RCLD-06
-
-- [x] 25. Pin Nostr dependency and implement key generation/derivation adapter.
-- [x] 26. Implement generate account command with in-memory storage.
-- [x] 27. Implement import secret key command.
-- [x] 28. Define and test duplicate import and credential-repair handling.
-- [x] 29. Implement add/import transaction rollback across keyring and DB.
-- [x] 30. Implement persisted generate/import using SQLite adapter.
-
-### RCLD-07
-
-- [x] 31. Implement select account command.
-- [x] 32. Implement activate account with safe replacement ordering.
-- [x] 33. Implement sign out command.
-- [x] 34. Implement revision-bound removal request/confirmation flow.
-- [x] 35. Implement removal journal recovery.
-
-### RCLD-08
-
-- [x] 36. Add relay configuration source and environment parser.
-- [x] 37. Implement Nostr event verification and kind-0 parsing adapter.
-- [x] 38. Add Nostr client port implementation and local relay fixture.
-- [x] 39. Implement cache-first active profile refresh orchestration.
-- [x] 40. Expose manual refreshActiveProfile command.
-
-### RCLD-09
-
-- [x] 41. Create UniFFI scaffolding and DTO mapping.
-- [x] 42. Expose approved AppCore commands through UniFFI.
-- [x] 43. Expose observer callback and deregistration through UniFFI.
-- [x] 44. Build native library artifacts from Cargo.
-- [x] 45. Add Gradle task for Cargo build.
-- [x] 46. Add Gradle UniFFI binding generation and generated source set.
-- [x] 47. Stage native library for development, tests, and packaged app.
-
-### RCLD-10
-
-- [x] 48. Add Kotlin StudioAppStore thin adapter.
-- [x] 49. Bootstrap AppCore once at application root.
-- [x] 50. Create UI model mapping helpers without server fields.
-- [x] 51. Implement inactive account screen generate/import controls.
-- [x] 52. Implement generated-key backup panel.
-- [x] 53. Implement saved-account list, activation, and removal controls.
-- [x] 54. Implement active account home screen.
-- [x] 55. Implement switch account flow in UI.
-- [x] 56. Remove canonical Kotlin account reducer/store usage.
-- [x] 57. Remove generic remote-account remnants.
-
-### RCLD-11
-
-- [x] 58. Add full local-relay end-to-end integration test.
-- [x] 59. Add restart, account isolation, and FFI callback integration tests.
-
-### RCLD-12
-
-- [x] 60. Add dependency and license documentation.
-- [x] 61. Complete architecture, security, testing, and runbook documentation.
-- [x] 62. Add Makefile-governed final validation tasks and platform ledger. Do
- not add `.github/**` or `scripts/**`.
-- [x] 63. Perform final source audit and acceptance reconciliation.
-
-## RCLD completion ledger
-
-- [x] RCLD-01: Authority and dependency baseline.
-- [x] RCLD-02: Rust workspace and domain.
-- [x] RCLD-03: Application state machine.
-- [x] RCLD-04: SQLite persistence.
-- [x] RCLD-05: Credential boundary.
-- [x] RCLD-06: Account generation and import.
-- [x] RCLD-07: Account lifecycle and recovery.
-- [x] RCLD-08: Relay and profile runtime.
-- [x] RCLD-09: UniFFI and native build integration.
-- [x] RCLD-10: Thin Kotlin shell and minimal UI.
-- [x] RCLD-11: End-to-end integration hardening.
-- [x] RCLD-12: Documentation and acceptance reconciliation.
diff --git a/docs/runbooks/local-relay-development.md b/docs/runbooks/local-relay-development.md
@@ -1,40 +0,0 @@
-# Local relay development
-
-Rust reads the ordered comma-separated relay list from
-`RADROOTS_NOSTR_RELAYS`, trims entries, validates the WebSocket policy, and
-deduplicates while preserving first-seen order. Development and tests may use
-`ws://localhost:8080` when the variable is absent or empty. Packaged builds
-have no fallback and report `InvalidRelayConfiguration` until at least one
-valid relay is configured.
-
-The Rust integration lane starts an in-process relay on an ephemeral loopback
-port, publishes a signed kind-0 event, fetches it through the production SDK
-adapter, and shuts both clients and the relay down before returning. Run it
-without any public-network dependency:
-
-```sh
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-application sdk_client
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-storage local_relay_e2e
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-ffi ffi_callback_receives_async_profile_refresh
-```
-
-Development and tests use local WebSocket relays only. The default development
-endpoint is `ws://localhost:8080`; tests bind ephemeral loopback ports. Override
-the ordered relay list with comma-separated `RADROOTS_NOSTR_RELAYS` values.
-
-Plain `ws://` is accepted only for `localhost`, `127.0.0.0/8`, and `::1`.
-Production remote endpoints require `wss://`. Tests must fail closed rather
-than contact public relays when a local fixture is unavailable.
-
-For interactive development, either start a relay at
-`ws://localhost:8080` or set `RADROOTS_NOSTR_RELAYS` before `make dev` or
-`make run`. Use a comma-separated ordered list when testing more than one
-relay. Do not put credentials in this variable. The development launcher sets
-the native core to development mode; packaged applications use packaged mode
-and therefore require explicit valid relay configuration.
-
-If refresh fails, inspect the safe relay/profile state in the active home
-screen. Cached metadata remains visible. Invalid configuration is corrected by
-restarting with a valid environment value; there is no in-app relay editor in
-this MVP. Local test failures should be reproduced with the focused commands
-above before broad workspace validation.
diff --git a/docs/security/key-management.md b/docs/security/key-management.md
@@ -1,67 +0,0 @@
-# Nostr key management
-
-## Status
-
-Implemented MVP security contract.
-
-## Storage boundary
-
-Production Nostr secret keys are stored only through Rust `SecretStore` in the
-OS credential store. The service is `org.radroots.studio.nostr`; the credential
-account key is canonical public-key hex. There is no ordinary-file fallback.
-
-The production adapter uses keyring 4.1.6 with the native macOS Keychain,
-Windows Credential Manager, or freedesktop Secret Service backend selected by
-the crate. Unsupported, locked, inaccessible, malformed, and platform-failure
-outcomes become a stable `KeyringUnavailable` error. No alternative storage is
-attempted. The real keyring smoke test is ignored by default because it mutates
-the invoking user's credential store and must be run explicitly on each target.
-
-Secrets are forbidden in SQLite, profile cache, operation journals, public
-snapshots, normal DTOs, logs, errors, filenames, preferences, fixtures, and
-golden files. Rust application secret wrappers are non-cloneable, redacted,
-non-serializable values.
-
-Generated nsec is returned once in a direct operation receipt, displayed in
-non-saveable Kotlin state, and cleared after acknowledgement or disposal.
-Explicit copy places the nsec in the operating-system clipboard, which is an
-additional user-authorized exposure. A lifecycle-owned timer clears it after
-60 seconds only if the clipboard still contains the copied value; user-replaced
-content is preserved. Application disposal performs the same conditional clear.
-Imported key input crosses an unavoidable JVM `String` boundary once;
-the masked Compose draft is cleared immediately when the command is accepted,
-before the native coroutine executes. The in-flight JVM argument cannot be
-guaranteed zeroized and is never logged or added to public state.
-
-Keyring unavailability is a safe recoverable error. Cross-resource operations
-publish no partial success and use a non-secret journal for restart recovery.
-
-The journal contains only an operation identifier, operation kind, canonical
-public key, phase, safe timestamp, and optional safe diagnostic code. It cannot
-store credential text or arbitrary payloads, and it survives account metadata
-removal until cleanup has been finalized.
-
-Workspace redaction tests scan public snapshot and safe-error debug output plus
-the SQLite schema and representative durable records for known secret-hex,
-nsec, and secret-prefix fixtures. These guards run before account commands are
-allowed to carry production credentials.
-
-Startup reads the non-secret operation journal before restoring public state.
-An empty journal performs no keyring operation. Removal recovery retries an
-intent, continues honestly from `CredentialDeleted`, completes metadata and
-account-namespace cleanup, persists deterministic fallback selection, and then
-finalizes the entry. Keyring failure leaves the phase unchanged for retry.
-
-Add/import records intent before credential creation. After credential write it
-records `CredentialWritten`, writes public metadata and selection, records
-`MetadataWritten`, then finalizes. A metadata failure attempts credential
-compensation; a failed compensation leaves enough non-secret journal state for
-startup recovery. Removal records intent, signs out if necessary, deletes the
-credential, records `CredentialDeleted`, deletes public and account-owned data,
-records `MetadataDeleted`, persists fallback selection, and finalizes.
-
-Corrupt or inaccessible SQLite fails safely and is not silently recreated.
-Locked or unavailable credentials do not become watch-only accounts. Platform
-credential behavior must be checked on macOS, Windows, and Linux; the real
-keyring smoke remains ignored by default because it mutates user credential
-state.
diff --git a/docs/testing/final-validation-ledger.md b/docs/testing/final-validation-ledger.md
@@ -1,87 +0,0 @@
-# Final validation ledger
-
-## Result
-
-Validation completed on 2026-08-02 for macOS 26.5 arm64. All 30 acceptance
-criteria in the authoritative handoff are satisfied by implemented source,
-tests, or an explicit documented platform-validation contract. No test uses a
-public relay.
-
-The Radroots crates release v1 reconciliation was rerun on 2026-08-03. Locked
-Rust formatting, workspace all-target checks, workspace all-target tests,
-Gradle desktop checks, native-loader coverage, and the current-host DMG package
-were green through the governed extbuild output router. The reviewed legacy SDK
-runtime is absent; the evidence-based Step 274–278 deviation is recorded in
-`docs/architecture/radroots-crates-release-v1-reconciliation.md`.
-
-## Acceptance reconciliation
-
-| # | Result | Evidence |
-| --- | --- | --- |
-| 1 | Pass | `core/Cargo.toml` defines the Rust workspace and all five runtime crates build together. |
-| 2 | Pass | `radroots-studio-application::AppCore` owns canonical snapshots, transitions, commands, observers, sessions, recovery, and refresh orchestration. |
-| 3 | Pass | `StudioAppStore` maps generated UniFFI DTOs into presentation models and forwards commands without duplicating the reducer or persistence policy. |
-| 4 | Pass | Domain, persistence, FFI, and Kotlin models use canonical lowercase 64-character Nostr public-key hex; forbidden UUID guards are clean. |
-| 5 | Pass | The generate command, UniFFI method, Compose control, and behavior tests create a persisted local Nostr key. |
-| 6 | Pass | Masked nsec/hex import is implemented through the Rust key boundary and covered by valid, invalid, duplicate, and repair tests. |
-| 7 | Pass | Storage and Compose tests cover multiple saved accounts; the chooser remains scrollable within the available window. |
-| 8 | Pass | Activation is available for each saved account and Rust tests prove safe session replacement. |
-| 9 | Pass | Sign-out drops the active signer/session while retaining metadata, selection, and credential. |
-| 10 | Pass | Removal uses a single-use target-and-revision-bound confirmation token, deterministic fallback, and recovery journal. |
-| 11 | Pass | Production wiring uses `OsKeyringSecretStore` with service `org.radroots.studio.nostr` and no file fallback. |
-| 12 | Pass | Redaction, schema-byte, DTO, snapshot, error, journal, and source guards keep secrets out of forbidden surfaces. |
-| 13 | Pass | Generated nsec exists only in `GenerateAccountReceipt` and the transient backup UI; acknowledgement, replacement, timeout, and disposal clear it. |
-| 14 | Pass | SQLite restart tests restore account metadata and selection while startup remains signed out. |
-| 15 | Pass | Typed repository and restart-isolation tests prove account-local values are partitioned by owner pubkey. |
-| 16 | Pass | Production relay configuration is read from `RADROOTS_NOSTR_RELAYS`. |
-| 17 | Pass | Development mode alone defaults to `ws://localhost:8080`. |
-| 18 | Pass | Relay parsing accepts governed WebSocket URLs only; generic account-server terms and fields are absent. |
-| 19 | Pass | Activation emits cached profile state before verified kind-0 refresh; failure and stale completion preserve safe state. |
-| 20 | Pass | The active screen renders `radroots`, pubkey, npub, bounded profile metadata, relay list, and profile/relay status. |
-| 21 | Pass | Source guards find no generic server URL field or onboarding language. |
-| 22 | Pass | The previous Kotlin `AccountsReducer` and `AccountsStore` are absent; `StudioAppStore` is an FFI adapter only. |
-| 23 | Pass | `make check` passed Rust, FFI, storage, security, restart, local-relay, Kotlin-store, native-loader, and Compose UI lanes. |
-| 24 | Pass | The RCLD history contains ordered, independently verified commit-sized checkpoints plus separately committed audit fixes. |
-| 25 | Pass | Source, tests, generated-boundary policy, package contents, forbidden terms, and all handoff criteria were audited at checkpoint 63. |
-| 26 | Pass | The command ledger below distinguishes executed checks from intentionally skipped interactive, destructive, or foreign-platform checks. |
-| 27 | Pass | `docs/architecture/reference-research.md` records reviewed revisions, paths, adopted/rejected patterns, license boundaries, and dependency decisions. |
-| 28 | Pass | Required ADR, architecture, security, testing, and local-relay runbook documentation exists and matches the runtime. |
-| 29 | Pass | `docs/testing/platform-validation.md` defines the current-host evidence and the required Linux/Windows loader, keyring, and packaging matrix. |
-| 30 | Pass | Network tests use an ephemeral loopback relay; configuration tests do not contact the network. |
-
-## Commands executed
-
-| Command | Result |
-| --- | --- |
-| `make check` | Passed Rust formatting, all-target Clippy with denied warnings, all workspace tests, desktop tests, and Gradle check. |
-| `make build` | Passed the Rust workspace and Compose Desktop build. |
-| `make bindings` | Passed UniFFI Kotlin generation and current-host native-library staging. |
-| `make package` | Produced `app/desktop/build/compose/binaries/main/dmg/Radroots-1.0.0.dmg`. |
-| `codesign --verify --deep --strict .../Radroots.app` | Passed. |
-| `PlistBuddy` bundle inspection | Confirmed bundle ID `org.radroots.studio` and installer version `1.0.0`. |
-| Packaged application JAR inspection | Confirmed `darwin-aarch64/libradroots_studio_ffi.dylib`, `icons/radroots.icns`, and `icons/radroots.png`. |
-| Tracked-path guards | Found no build output, generated UniFFI source, native binary, `.github/**`, or `scripts/**` path. |
-| Forbidden-term guards | Found no old package/version, UUID account, Kotlin reducer/store, generic account-server, or server-URL term. |
-| Nested-repository status and history inspection | Confirmed the standalone capsule boundary and local commit sequence. |
-
-`make check` executed 35 application tests plus its redaction test, 21 domain
-tests, 6 FFI tests, 6 Nostr tests, 19 passing storage unit tests, three storage
-integration tests, the bindgen test, all documentation tests, and the complete
-Kotlin/Compose test task. The one ignored storage test is the intentionally
-opt-in real operating-system keyring smoke.
-
-## Intentionally not executed
-
-- The real OS keyring smoke was not enabled because it mutates the current
- user's credential store. Its adapter contract tests passed.
-- Linux and Windows loader, keyring, and packaging checks cannot run on this
- macOS host. Their required native-host matrix is recorded in
- `platform-validation.md`.
-- `make dev` and `make run` are interactive launchers and were not held open.
-- `make clean` was not run because it only destroys recoverable build output
- and is not an acceptance behavior.
-
-The application/runtime artifacts retain version `0.1.0-alpha`. The macOS
-installer uses `1.0.0` because `jpackage` rejects the prerelease form; this
-mapping is deliberate and documented. No source-level acceptance blocker
-remains.
diff --git a/docs/testing/nostr-accounts-test-plan.md b/docs/testing/nostr-accounts-test-plan.md
@@ -1,67 +0,0 @@
-# Nostr accounts test plan
-
-## Status
-
-Implemented test contract. There is no numeric coverage threshold; the suite
-uses behavior-focused best-effort coverage and fails if no Kotlin tests are
-discovered.
-
-## Required lanes
-
-- Rust domain validation, canonicalization, state invariants, and transition
- tables.
-- SQLite migrations, restart restoration, account isolation, corruption, and
- transaction failure injection.
-- SecretStore contract, platform smoke tests, and no-secret guards.
-- Nostr key vectors, signed-event verification, kind-0 ordering, and bounded
- profile parsing.
-- Local ephemeral relay, cached-first refresh, timeout, invalid data,
- cancellation, and stale-result behavior.
-- UniFFI generation, DTO mapping, callback ordering/re-entry/deregistration,
- object disposal, and native loading.
-- Kotlin store lifecycle and Compose UI coverage for generate, import, backup,
- selection, activation, refresh, switch, sign out, removal, and safe errors.
-- End-to-end restart, recovery, account isolation, and packaged-runtime smoke.
-
-No test uses a public relay. There is no numeric coverage threshold; tests must
-provide strong best-effort behavioral coverage throughout RCL development.
-
-## Rust validation gates
-
-Run from the capsule root:
-
-```sh
-cargo fmt --manifest-path core/Cargo.toml --all --check
-cargo clippy --manifest-path core/Cargo.toml --workspace --all-targets -- -D warnings
-cargo test --manifest-path core/Cargo.toml --workspace
-```
-
-Run the desktop, generated binding, native loader, store, and Compose lanes:
-
-```sh
-./gradlew --no-daemon :app:desktop:test
-```
-
-Focused integration paths are:
-
-```sh
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-storage local_relay_e2e
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-storage restart_restores_selection
-cargo test --manifest-path core/Cargo.toml -p radroots-studio-ffi ffi_callback_receives_async_profile_refresh
-./gradlew --no-daemon :app:desktop:test --tests org.radroots.studio.ffi.NativeLoaderTest
-```
-
-`local_relay_e2e.rs` imports and activates a deterministic signer, reads signed
-kind-zero metadata through an ephemeral loopback relay, observes loading/fresh
-revisions, verifies SQLite cache, and checks public redaction.
-`restart_isolation.rs` reopens the database, restores selected accounts, proves
-owner-scoped values remain isolated, and scans bytes for known secrets. FFI
-tests cover callback re-entry, asynchronous refresh, unsubscribe, and shutdown.
-Compose tests cover the complete minimal UI surface and use fake actions rather
-than credentials or public relays.
-
-The real OS keyring smoke test is ignored by default and must be explicitly run
-on each supported target in an isolated test account. Packaging and native
-loader smoke are current-host checks; cross-platform results belong in the
-final validation ledger. The capsule intentionally has no `.github/**` workflow
-and no `scripts/**` command surface.
diff --git a/docs/testing/platform-validation.md b/docs/testing/platform-validation.md
@@ -1,62 +0,0 @@
-# Platform validation ledger
-
-## Command authority
-
-The capsule root `Makefile` is the complete human-facing lifecycle. It invokes
-Cargo with `core/Cargo.toml` explicitly and invokes the checked-in Gradle
-wrapper. This standalone `oss/**` repository does not use extbuild, validation
-scripts, or `.github/**` workflows.
-
-| Target | Contract |
-| --- | --- |
-| `make doctor` | report Java, Cargo, and Gradle toolchains |
-| `make format` | check Rust formatting |
-| `make lint` | deny Rust workspace and all-target Clippy warnings |
-| `make test` | run the full Rust and Kotlin/Compose/native-loader suites |
-| `make check` | run format, lint, test, and Gradle check |
-| `make build` | build the Rust workspace and desktop application |
-| `make bindings` | generate Kotlin bindings and stage the host native library |
-| `make dev` | launch Compose hot reload in development relay mode |
-| `make run` | launch Compose Desktop in development relay mode |
-| `make package` | build the current-host desktop distribution |
-| `make clean` | remove Gradle and Cargo build outputs |
-
-## Current-host result
-
-Validation date: 2026-08-02.
-
-- Host: macOS 26.5 build 25F71, arm64.
-- Java: Eclipse Temurin 21.0.11 LTS.
-- Cargo: 1.97.1 with the workspace Rust 1.97 toolchain contract.
-- Gradle: wrapper 9.5.0.
-- Native resource: `darwin-aarch64/libradroots_studio_ffi.dylib`.
-- Package format: macOS DMG with application name `Radroots`, bundle ID
- `org.radroots.studio`, installer-compatible version `1.0.0`, and the
- generated squircle application icon. macOS `jpackage` rejects a leading-zero
- app version, so this field cannot encode `0.1.0`; application and runtime
- artifacts keep the full `0.1.0-alpha` prerelease version.
-
-The final validation checkpoint records the exact green command results and
-artifact inspection. Interactive `make dev` and `make run` are launchers and
-are reviewed but not held open during automated validation. `make clean` is
-destructive to recoverable build outputs and is not required for acceptance.
-
-Checkpoint 62 results:
-
-| Command | Result |
-| --- | --- |
-| `make check` | passed Rust format, all-target Clippy, Rust workspace tests, Kotlin/Compose/native-loader tests, and Gradle check |
-| `make build` | passed Rust workspace and desktop builds |
-| `make bindings` | passed UniFFI generation and current-host native staging |
-| `make package` | produced `Radroots-1.0.0.dmg` successfully |
-| packaged app inspection | bundle ID and installer version matched; code signature verified; application jar contained the arm64 dylib and both icon resources |
-
-## Remaining platform matrix
-
-Linux x86-64/aarch64 and Windows x86-64/aarch64 resource-prefix selection is
-implemented but not validated on this macOS host. Each platform must run
-`make check`, `make build`, `make bindings`, the real keyring smoke in an
-isolated credential account, and its native packaging smoke. A package must
-contain only its matching JNA resource prefix and native filename. Cross-built
-artifacts are not accepted as substitutes for host loader and credential-store
-tests.