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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ jobs:
node --test runtime/http-failure-diagnostic.test.mjs runtime/sync-handoff-host-uuid.test.mjs
node --test runtime/prepared-effect.test.mjs runtime/email-domain-boundary.test.mjs runtime/integration-source.test.mjs runtime/capture-transport.test.mjs
node --test runtime/smtp-send-runner.test.mjs runtime/run-inspection-runtime.test.mjs
node --test runtime/installed-channels.test.mjs runtime/runtime-channel-install-process.test.mjs
node --test runtime/installed-channels.test.mjs runtime/runtime-channel-install-process.test.mjs runtime/reattach-v2.test.mjs runtime/registration-continuity-process.test.mjs
- name: Start the standalone exported Runtime artifact
run: npm run test:runtime:artifact
2 changes: 1 addition & 1 deletion app/release-package.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ test('fresh package authenticates its manifest and worker artifact against publi
assert.equal(metadata.version, '1.9.1-preview.1');
assert.equal(compatibility.email.tag, 'v' + rootMetadata.version);
assert.equal(compatibility.email.app_version, metadata.version);
assert.equal(compatibility.host.minimum_version, '0.3.0-preview.127');
assert.equal(compatibility.host.minimum_version, '0.3.0-preview.131');
assert.equal(compatibility.host.version, compatibility.host.minimum_version);
const artifact = JSON.parse(readFileSync(new URL('./artifact.json', import.meta.url)));
assert.equal(JSON.parse(artifact.content).background_refresh_description, "Keep mailbox metadata current while this app is closed. Turn this on to create Tasks or Knowledge from mail: it is how the app shares messages with Deft.");
Expand Down
12 changes: 6 additions & 6 deletions release/compatibility.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
"authoritative_release_manifest": "https://github.com/Maneek21/deft-email/releases/download/v1.9.0-preview.20/compatibility.json",
"resolution": "The detached preview.20 release asset resolves exact tagged source commits, Host preview.127 image digest and asset SHA256 values after packaging. This source template does not certify publication or contain its own archive checksum.",
"host": {
"version": "0.3.0-preview.127",
"minimum_version": "0.3.0-preview.127",
"tag": "v0.3.0-preview.127",
"version": "0.3.0-preview.131",
"minimum_version": "0.3.0-preview.131",
"tag": "v0.3.0-preview.131",
"repository": "https://github.com/Maneek21/Deft",
"release_manifest": "https://github.com/Maneek21/Deft/releases/download/v0.3.0-preview.127/release-manifest.json",
"release_manifest": "https://github.com/Maneek21/Deft/releases/download/v0.3.0-preview.131/release-manifest.json",
"browser_profile": "apps-developer-preview",
"schema_version": "0.3.0-preview.120",
"schema_version": "0.3.0-preview.131",
"contracts": { "app_protocol": 7, "connection_protocol": 2, "record_form": 3, "resource_sync_channel": 3 }
},
"email": {
Expand All @@ -20,7 +20,7 @@
"runtime_version": "0.4.0-preview.18",
"app_kit_version": "0.1.0-alpha.39",
"runtime_kit_version": "0.1.0-alpha.35",
"kit_compatibility": "App alpha.39 supports the app-owned background_refresh_description field, requiring Host preview.127 or newer. Runtime alpha.35 keeps its existing transport contracts; schema120 is unchanged. The Kit versions need not match."
"kit_compatibility": "App alpha.39 supports the app-owned background_refresh_description field, requiring Host preview.127 or newer. Runtime alpha.35 keeps its existing transport contracts; Host preview.131 supplies scoped service reacquisition and trusted upgrade continuity. The Kit versions need not match."
},
"kits": [
{ "version": "0.1.0-alpha.39", "asset": "deft-app-kit-0.1.0-alpha.39.tgz", "sha256": "5d45f6cb9cdf4ffc238e1137609dda5f32bbf48f2ebd2d0e47fb50ac62622a5f", "consumer": "email-app", "source_tag": "v0.3.0-preview.127", "source_repository": "https://github.com/Maneek21/Deft" },
Expand Down
2 changes: 1 addition & 1 deletion runtime/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ prepare_action is data preparation only: it validates current row/content/files/

One send_message binding handles compose/reply/reply_all/forward; native reply headers/thread and all bytes stay pinned. The runner validates exact started claim/current authorize response, owner/org/install/version/grant/epochs/binding/lease, prepared/input digests, local connected custody/client/account and conditional Send consent. It atomically freezes the exact draft CAS as submitted, then reserves snapshot provider_started before dispatch. Concurrent edits/prepares cannot reuse that draft. Every provider I/O revalidates public current action authority and shrinking lease. Validated provider acceptance is persisted before final disclosure, with app-owned receipt and accepted summary; unknown effect remains submitted/unknown or started. The public reviewed Runtime result is accepted/indeterminate/not_attempted, never a claim of delivery.

Process loss/provider uncertainty/lease expiry never resends a reserved snapshot. Repeated exact Run can return retained acceptance; foreign Run cannot acquire it. Snapshot/request journals and byte custody survive restart. Orphan .writer lease is never automatically stolen: fence all old writers, verify recorded process dead, snapshot full state+key, remove only orphan lease, then start one writer and reconcile. Preserve unknown rows/tombstones/custody; no implicit reconnect, key export, ciphertext adoption or automatic effect repair.
Process loss/provider uncertainty/lease expiry never resends a reserved snapshot. Repeated exact Run can return retained acceptance; foreign Run cannot acquire it. Snapshot/request journals and byte custody survive restart. The writer holds a process-lifetime SQLite exclusive file lock. Hard crashes release it automatically; new writers create no .writer marker, so a kill during bootstrap cannot strand ownership metadata. Stop legacy Runtime processes before the first upgrade; a live legacy PID remains a refusal. Never delete .writer-lock.sqlite while a Runtime is running. Preserve unknown rows/tombstones/custody; no implicit reconnect, key export, ciphertext adoption or automatic effect repair.

## Evidence boundaries and remaining work

Expand Down
19 changes: 13 additions & 6 deletions runtime/PROVENANCE.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@
{
"source": "examples/apps/email/providers/oauth-account.mjs",
"target": "vendor/providers/oauth-account.mjs",
"sha256": "10c4b0c6202e0ce331dee56e9fed6253f053345f8be7dfe8b30814554d40f564",
"sha256": "55d3c6caeeb2f74d2d9a30cfad170bcbfd1211cb671699a58ac0a3f27aa90fd1",
"mode": "reviewed_adaptation",
"e5_parent_sha256": "3433d090c0e05617e03312231f17c9b6a7cfcd692907038316e0bb2f51e3cc17",
"frozen_source_sha256": "3433d090c0e05617e03312231f17c9b6a7cfcd692907038316e0bb2f51e3cc17",
"e5_merge_reason": "Reviewed original-native import preserves immutable custody, native namespace and inspect-only effect history on exact accepted current E4 parent.",
"e45_parent_sha256": "4b642c34261e94d85ea9cecc9636d056d056476ebb164c216f0a00986464daf4",
"e45_merge_reason": "Exact credential repair validates unchanged provider identity/client/granted scopes before credential custody write.",
"e58_parent_sha256": "723beced6f5aa9aecf9a93dc788914c9fe8f44571a39a8ff366403fa7a3d643c",
"e58_merge_reason": "Accepted optional secret-free credential-repair completion stage diagnostics; ordinary OAuth behavior unchanged."
"e58_merge_reason": "Accepted optional secret-free credential-repair completion stage diagnostics; ordinary OAuth behavior unchanged.",
"p3_parent_sha256": "10c4b0c6202e0ce331dee56e9fed6253f053345f8be7dfe8b30814554d40f564",
"p3_adaptation": "Reviewed Gmail-only durable refresh reconciliation: retain last usable refresh token, commit received credentials before identity verification, retry transport/verification with encrypted backoff, and keep identity, scope, live authority and Microsoft uncertain-rotation fences."
},
{
"source": "examples/apps/email/providers/oauth-contract.mjs",
Expand All @@ -38,17 +40,22 @@
{
"source": "examples/apps/email/providers/oauth-provider.mjs",
"target": "vendor/providers/oauth-provider.mjs",
"sha256": "8144e4db2cf56f9ff2ffa41efcb2a811497778db8d30354ae9c148cc092e85b7",
"mode": "byte_copy"
"sha256": "af47de79c914518352b34bc70411ca023d056a07c110d7446a37626d82e91db6",
"mode": "reviewed_adaptation",
"p3_parent_sha256": "8144e4db2cf56f9ff2ffa41efcb2a811497778db8d30354ae9c148cc092e85b7",
"frozen_source_sha256": "8144e4db2cf56f9ff2ffa41efcb2a811497778db8d30354ae9c148cc092e85b7",
"p3_adaptation": "Reviewed Gmail-only durable refresh reconciliation: retain last usable refresh token, commit received credentials before identity verification, retry transport/verification with encrypted backoff, and keep identity, scope, live authority and Microsoft uncertain-rotation fences."
},
{
"source": "examples/apps/email/providers/custody-encrypted.mjs",
"target": "vendor/providers/custody-encrypted.mjs",
"sha256": "fc67a4833bb7254fe4687318c43243d11c845b836f6bc46bd5851bf848c90e77",
"sha256": "fe89775f32a37a21d8f38be0fb99a53945f79ff5b9428c972da0cfb999e73779",
"mode": "reviewed_adaptation",
"e5_parent_sha256": "dd3b60d0024f31ae316188f5b36175d7660abd15ed9d2133fbb58d8a74fbe022",
"frozen_source_sha256": "dd3b60d0024f31ae316188f5b36175d7660abd15ed9d2133fbb58d8a74fbe022",
"e5_merge_reason": "Reviewed original-native import preserves immutable custody, native namespace and inspect-only effect history on exact accepted current E4 parent."
"e5_merge_reason": "Reviewed original-native import preserves immutable custody, native namespace and inspect-only effect history on exact accepted current E4 parent.",
"p3_parent_sha256": "fc67a4833bb7254fe4687318c43243d11c845b836f6bc46bd5851bf848c90e77",
"p3_adaptation": "Reviewed Gmail-only durable refresh reconciliation: retain last usable refresh token, commit received credentials before identity verification, retry transport/verification with encrypted backoff, and keep identity, scope, live authority and Microsoft uncertain-rotation fences."
},
{
"source": "examples/apps/email/providers/custody-file-store.mjs",
Expand Down
15 changes: 15 additions & 0 deletions runtime/UNATTENDED-RECOVERY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Unattended channel and Gmail recovery

Release the Deft Host service-fetch route before this Runtime. No database migration or new operator secret is needed. Existing until-revoked account consent and the operator-configured service credential are the durable opt-in; the legacy renewal SID field stores a service-registration provenance label, while current human membership/role, deployment admission, installation, account grant and reviewed bindings remain live requirements. Finite permissions and per-version registration changes retain their existing review/expiry behavior; this work never extends a finite permission or carries a grant across an unreviewed update.

The design reuses current authority resolution and authenticated Host-to-Runtime delivery. An independent enrollment database would duplicate those checks and need a migration; extending bearer expiry would still fail after sufficiently long downtime. The service route accepts only the configured registration, authenticates its authority-channel credential, derives the installation from the Host, and requires owner=operator and current durable consent. No prior technical session row is needed. It cannot choose another owner or create a grant. The existing installer APIs remain compatible.

Renewal begins 5–6 minutes before a technical lease ends. Claims remain closed while lease authority is uncertain. Expired or denied leases trigger service reconciliation for operator-configured registrations. The Runtime retries transient failures indefinitely with exponential jitter (about one second, capped at five minutes), independent of the expired lease. A service-authentication/permission denial emits `reconnect_required`; a later valid owner installation resets recovery. Installation ACKs contain identifiers only. The encrypted installed-channel journal is authoritative after lost ACKs and restart. Superseded workers stop; no start, result, provider effect or Send operation is replayed.

Gmail refresh is serialized by the existing account custody lock. Encrypted `refresh_reconciliation` records distinguish `transport_unavailable` and `verification_pending`, with a durable next retry time. A received token response is committed before post-request authority checks and identity GETs. Safe retries retain the newest credentials; missing replacement refresh tokens retain the prior token. Legacy Gmail `refresh_pending` rows become eligible for reconciliation only through operational read custody; upgrade and credential-repair readers retain the original pending fence. The public account remains a connected grant while token access is pending; no token is returned until identity and live authority checks succeed. Actual `invalid_grant`, identity mismatch and removed required scope require reconnect. Microsoft lost rotating-token responses retain their uncertain-outcome fence.

The writer's `.writer-lock.sqlite` contains no application data. A lifetime `BEGIN EXCLUSIVE` transaction holds the OS file lock on supported local filesystems, including across containers sharing the same filesystem. New writers create no .writer marker; crashes during bootstrap, read or commit release the lock. An active writer cannot be displaced by a TTL or recycled PID. Application row encryption, fsync/atomic rename, revision CAS and Send journals are unchanged. Preserve the SQLite lock file path/inode while any writer runs. Existing private-root and unsupported-network-filesystem checks remain in force. Windows remains a local fixture mode, with the existing durability limitation.

Regression coverage includes extended Host/Runtime outage, stale local metadata, renewal/install ACK loss, transient/permanent service failure, concurrent refresh, refresh/verification timeout, persisted-token restart, legacy pending state, invalid_grant, live revocation, hard kill around read/commit and second-writer exclusion. All use synthetic providers; they do not establish a live Gmail or deployment result.

Host preview.131 or newer is required. Trusted App upgrades create fresh version-bound registration IDs. The Runtime accepts such a successor only through v2 reattachment from a previously enrolled source, after the authenticated Host authority and existing exact custody/purpose checks succeed. A durable prepared-transition index precedes the prepare ACK; a connected journal and exact receipt enroll the successor. Restart reconstructs that routing enrollment, including activation whose ACK was lost. The service supervisor nominates this current registration, preserving the Host service endpoint's exact-ID fence. Channel installation, data access and service reacquisition use the enrolled current registration without operator-file edits. This routing state never replaces live Host authorization, extends finite permissions, or retries provider effects.
Loading
Loading