Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
36d006a
Read independent ACK sources with the native receipt writer
qh-work Sep 30, 2026
6d36f3f
Upload native saved receipts and retain interoperable recovery journals
qh-work Sep 30, 2026
83011b9
Stop ACK upload when current permission expires during journal persis…
qh-work Sep 30, 2026
5c2594d
Replay native receipt journals across restarts without renewing autho…
qh-work Sep 30, 2026
e383f65
Return actual saved receipts through the native Agent and shared dura…
qh-work Sep 30, 2026
2479cf1
Align alpha35 plugin version and include native writer review fixtures
qh-work Sep 30, 2026
751978a
Authenticate native mailbox owner controls and persisted resource act…
qh-work Sep 30, 2026
21d59cb
Authenticate native mailbox admission and complete retained feed history
qh-work Sep 30, 2026
d86d620
Keep exact public SDK inventory aligned with native receipt return
qh-work Sep 30, 2026
08626c6
Resume retained mailbox memory imports and saved receipts in native A…
qh-work Sep 30, 2026
18cfccf
Merge branch 'feat/native-ack-offer' into feat/native-mailbox-read
qh-work Sep 30, 2026
c052c5a
Receive registered remote mailbox memories in native Agent
qh-work Sep 30, 2026
325c867
Merge published alpha35 baseline into native mailbox recovery
qh-work Sep 30, 2026
13fcdcf
Merge branch 'main' of https://github.com/qh-work/memory-vault-sync i…
qh-work Sep 30, 2026
0614aa4
Prepare alpha36 native remote mailbox recovery candidate
qh-work Sep 30, 2026
5945c9a
Bound the expanded native mailbox release inventory at 640 files
qh-work Sep 30, 2026
bd3ca97
Align alpha36 plugin and installation guides with mailbox recovery
qh-work Sep 30, 2026
b82d66b
Return retained cold mailbox receipts during native Agent receive
qh-work Sep 30, 2026
4734a1c
Merge published alpha36 baseline preserving native receipt returns
qh-work Sep 30, 2026
0b6b1c4
Prepare alpha37 automatic native mailbox receipt release
qh-work Sep 30, 2026
fe943c6
Merge branch 'main' of https://github.com/qh-work/memory-vault-sync i…
qh-work Sep 30, 2026
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
24 changes: 24 additions & 0 deletions clients/typescript/network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,27 @@ bounded feed/body recovery, and `readMailboxIndex`, `readMailboxAdmission`, and
not delegate to a Python process. Native mailbox provisioning and replica inbox
recovery remain unfinished; use the Python client for those paths. These additions
are in the alpha.0.36 candidate; public release acceptance remains pending.

### Retained receipt destinations (development after alpha.0.36)

The native Agent accepts `memory-vault-open-ack-connect/v1` actions
`register_mailbox_receipt_return`, `list_mailbox_receipt_returns`,
`inspect_mailbox_receipt_return` and `remove_mailbox_receipt_return`.
Registration retains an exact `message_id`, `source_url`, `source_key_id` and
`repair_profile` (`receipt` or `receipt-index`) locally. It can happen before
reception; it grants no upload or disclosure permission.

Ordinary `receive` attempts up to two ready, saved-message returns within its
shared deadline. It reopens the mailbox evidence and original independent ACK
configuration, proves the selected source, and persists the exact preparation
before returning the actual saved receipt. A lost reply or process restart
resumes the same original request. Authenticated original-root status history
remains durable; retained denials stop a pending upload before retransmission.
Expired uncertain returns become `reconciliation_required` and stop polling.

The bounded selection and return journals use the same protected SQLite tables
as Python. Removing a selection preserves the saved message and original return
history. Re-registering a completed selection can reuse authenticated local
history without another upload. Direct `return_mailbox_receipt` supports the same
message and source selection. Native replica inbox support remains unfinished.
These additions are development source after the frozen alpha.0.36 candidate.
1 change: 1 addition & 0 deletions clients/typescript/network/open-ack-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Obj=Record<string,any>;
export const ACK_CONNECT_SCHEMA='memory-vault-open-ack-connect/v1';
const PROFILES={receipt:{...DEFAULT_REPAIR_CLIENT_LIMITS,max_signature_checks:2048,max_proof_bytes:1048576},
'receipt-index':{...DEFAULT_REPAIR_CLIENT_LIMITS,max_signature_checks:4096,max_proof_bytes:4194304,max_requests:256,max_replay_records:256}};
export {PROFILES as ACK_REPAIR_PROFILES};
function fail(code:string):never{throw new NetworkError(code);}
function decode(value:unknown):Obj{
const entry=objectFields(value,['raw','ref']);
Expand Down
5 changes: 4 additions & 1 deletion clients/typescript/network/open-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {loadClient} from './client-config.ts';
import type {Client} from './client-config.ts';
import {OpenDeliveryClient} from './open-delivery-client.ts';
import {RegisteredMailboxReceivers,MAILBOX_CONNECT_SCHEMA} from './open-mailbox-receivers.ts';
import {MailboxReceiptJobs,MAILBOX_RECEIPT_ACTIONS,returnMailboxReceipt} from './open-mailbox-receipts.ts';
import {OpenParticipant} from './open-participant.ts';
import {OpenContactClient,CONNECT_SCHEMA} from './open-contact-client.ts';
import type {SignedNode} from './open-control.ts';
Expand Down Expand Up @@ -39,8 +40,10 @@ export class OpenNetworkClient{
if(invitation!=null){
if(typeof invitation==='object'&&!Array.isArray(invitation)&&(invitation as any).schema_version===MAILBOX_CONNECT_SCHEMA)
return this.result(new RegisteredMailboxReceivers(this.participant,this.encryption,this.delivery()).connect(invitation));
if(typeof invitation==='object'&&!Array.isArray(invitation)&&(invitation as any).schema_version===ACK_CONNECT_SCHEMA)
if(typeof invitation==='object'&&!Array.isArray(invitation)&&(invitation as any).schema_version===ACK_CONNECT_SCHEMA){
if(MAILBOX_RECEIPT_ACTIONS.has((invitation as any).action))return this.result((invitation as any).action==='return_mailbox_receipt'?await returnMailboxReceipt(this.participant,this.encryption,this.delivery(),invitation):new MailboxReceiptJobs(this.participant,this.encryption,this.delivery()).connect(invitation));
return this.result(await ((invitation as any).action==='return_receipt'?returnSavedReceipt:recoverReceipt)(this.participant,this.encryption,this.delivery(),invitation));
}
if(typeof invitation!=='object'||Array.isArray(invitation)||(invitation as any).schema_version!==CONNECT_SCHEMA)fail('open_private_invitation_unsupported');
const result=await new OpenContactClient(this.participant,this.encryption).dispatch(invitation,requestId);
return this.result({...result,profile:'open-routing-v1',network_accessed:true,open_messaging_supported:true});
Expand Down
15 changes: 11 additions & 4 deletions clients/typescript/network/open-delivery-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,12 +347,12 @@ export class OpenDeliveryClient{
if(!Object.hasOwn(session,'mailbox'))return document(session,MAX_INBOX_SESSION_BYTES) as Obj;
return objectFields(session,['mailbox','authority','source_node']);
}
private async verifyMailboxInbox(session:Obj,row:Obj):Promise<void>{
private async verifyMailboxInbox(session:Obj,row:Obj):Promise<Obj>{
const verified=await verifyMailboxInboxEvidence(session.mailbox,row.envelope,{owner:{signing_key:validateSigningIdentity(this.participant.identity),
encryption_key:validateEncryptionIdentity(this.encryption)},encryptionIdentity:this.encryption,stagedAt:row.created_at});
if(verified.core.message_id!==row.message_id||session.mailbox.sender.signing_key.key_id!==row.sender)fail('network_inbox_identity_conflict');
const expected=Object.fromEntries([['request','contact.request'],['policy','contact.policy']].map(([name,role])=>[name,document(verified.setup.roles[role].raw)]));
if(!same(session.authority,expected))fail('network_inbox_identity_conflict');
if(!same(session.authority,expected))fail('network_inbox_identity_conflict');return verified;
}
private async finishInbox(messageId:string):Promise<Obj>{
const row=this.inbox(messageId);if(!row)fail('network_message_not_found');if(['saved','rejected'].includes(row.phase))return document(row.result,MAX_RESULT_BYTES);
Expand Down Expand Up @@ -389,8 +389,15 @@ export class OpenDeliveryClient{
db.prepare('UPDATE open_delivery_inbox SET receipt=? WHERE message_id=?').run(encoded,messageId);return receipt;
}));
}
/** Read the actually saved inbox receipt for an explicitly requested return.
* The caller cannot substitute receipt bytes or a saved-state flag. */
/** Reauthenticate the saved mailbox evidence before deriving return authority. */
async savedMailboxForAck(messageId:string):Promise<Obj>{
if(!/^msg_[0-9a-f]{64}$/.test(messageId))fail('repair_saved_tuple_mismatch');
const row=this.inbox(messageId);if(!row||row.phase!=='saved')fail('repair_receipt_not_saved');
const session=OpenDeliveryClient.inboxSession(row.session);if(!Object.hasOwn(session,'mailbox'))fail('open_ack_mailbox_configuration_missing');
const verified=await this.verifyMailboxInbox(session,row);if(!verified.setup.ack_configuration)fail('open_ack_mailbox_configuration_missing');
return {owner:session.mailbox.sender,roles:Object.fromEntries(Object.entries(verified.setup.roles).map(([n,e]:[string,any])=>[n,{ref:e.ref,raw:e.raw}]))};
}
/** Read the actual saved receipt; never accept substituted receipt bytes. */
async savedReceiptForAck(messageId:string,owner:Obj,expectedEnvelope:unknown):Promise<{raw:Uint8Array;ref:Obj}>{
if(!/^msg_[0-9a-f]{64}$/.test(messageId))fail('repair_saved_tuple_mismatch');
const row=this.inbox(messageId);if(!row||row.phase!=='saved')fail('repair_receipt_not_saved');
Expand Down
Loading
Loading